OpenMRS Conventions


(Redirected from Conventions)

This page is marked to be re-organized.
In the process of defining our conventions

Overview

This page describes common conventions to be used for the OpenMRS project. Specific topics should be referenced (linked) from this page. Our goal is not to create a long laundry list of rules, but to provide guidance such that if two developers were to create the same functionality, the resulting work would be 80% similar.

Developing Code for OpenMRS

Attribution

  • Attribution is to be done within commit comments.
  • When a committer is applying a patch, the author(s) of the patch should be attributed within the commit comment according to the the comment conventions.
  • Attribution (either author or contributing authors) should not be placed into source code. When encountered, such attribution should be removed by (or with permission from) the original author(s). Contributions that require attribution (i.e., author(s) demanding attribution within the code contributions) will be graciously refused or removed from the repository.
(NOTE: this attribution-free coding policy took effect after version 1.1, so you may see some attribution in the existing code; we are gradually removing attribution from our code base)

Code Style

  • We use the automatic formatting feature of Eclipse to generate consistently formatted code
    • Simply use control-shift-f to format a file
    • OpenMRS formatting file for Eclipse: OpenMRSFormatter.xml
      • Install at Window -> Preferences -> Java -> Code Style -> Formatter
    • OpenMRS code template file for Eclipse: OpenMRSCodeTemplate.xml
      • Install at Window -> Preferences -> Java -> Code Style -> Code Templates
  • Use control-shift-o for finding/formatting/organizing imports - Very useful!

Exception Handling Conventions

Create an intuitive hierarchy of exceptions within the package they belong, only creating new Exception classes for when the need to branch on different types of exception within code/webapp are needed (add as needed instead of making extra classes "just in case" for every possible variation).

For example:

 org.openmrs.api.APIException
 org.openmrs.api.PatientIdentifierException extends APIException
 org.openmrs.api.MissingPatientIdentifierException extends PatientIdentifierException

and later add:

 org.openmrs.api.DuplicatePatientIdentifierException extends PatientIdentiferException

when we realize the webapp needs to distinguish duplicates from invalid identifier exceptions.

Repository Conventions

Branches

Commit Comments

Code Review

Data Model Design

Naming tables

  • We've chosen to use all lowercase names with underscores (_) between words (e.g., patient_address)
  • Use the singular form (e.g. patient_address instead of patient_addresses) except where the singular form is a reserved word (e.g. users instead of user, since user is a SQL reserved word)
  • Where auditing of rows is needed, use the following attributes:
    1. creator (FK to users)
    2. date_created (datetime)
    3. changed_by (FK to users)
    4. date_changed (datetime)
    5. voided (boolean)
    6. voided_by (FK to users)
    7. date_voided (datetime)
    8. void_reason (varchar 255)

Special Table Name Suffices

  • _map is used for many-to-many relationship tables
  • _type is used for a table that categorizes objects in another table — e.g., location_type
  • _attribute is used for a table that extends another table dynamically — e.g. person_attribute
  • _property is used to define Java-ish key/value properties associated with a given object — e.g. user_property

Naming columns

  • Use lowercase with underscores (_) between words
  • Auto-generated numeric primary keys are <table name>_id (e.g., patient_id and encounter_id)

Global and User Properties

Property Names

  • All names must be globally unique amongst property names
  • Dot notation, similar to Java properties, with specificity increasing from left to right
  • Camel case or underscores to delineate words
  • No whitespace (spaces, tabs, carriage returns)
  • All properties for modules should begin with the module id + "."
  • All portlet properties should begin with the portlet name + "."

Modules

Naming Your Module

  • Module id should be all lowercase without spaces. We prefer no breaks in the word (e.g., basicmodule); however, you may use underscores if absolutely necessary.
    • The Module id should correspond with the unqualified top-level package for classes in the module. So, org.openmrs.module.basicmodule would have an id of basicmodule.
    • Avoid using a module id that conflicts with existing modules or tables that already exist in the data model — e.g., "concept" and "form" would be undesirable module ids, since there are existing tables with these names.
  • Module name should be concise (ideally less than 20-25 letters), may contain spaces and upper case characters, but should avoid punctuation.

Module Tables

  • Module table names should begin with the module id — e.g., all tables added for a the "formentry" module should have table names beginning with "formentry_*".
  • Be careful when naming your module to avoid conflicts with other modules or existing tables

Module Project Management

  • While module developers are welcome to use their own ticketing system, we encourage module developers to use the Developers#Submit_Bugs OpenMRS ticketing system to track bugs and enhancement requests.
  • Request a new Trac component for your module from Ben, Burke, or Paul (e-mail by name at openmrs.org).
  • Add milestones for your module to the OpenMRS roadmap using the naming convention: <module name> Module x.x — e.g., FormEntry Module 1.0. Make one milestone for the current version and one for the next version. Optionally, you can make a milestone entitled "<module name> Module Someday" for enhancements not anticipated in the next version.
  • Module authors are responsible for keeping tickets and milestones for their module up to date


Logging

  • Using logging — put the following line at the top of your class:
private Log log = LogFactory.getLog(MyClassName.class);
  • Logging exceptions — use the pattern:
log.debug("Concise title for debug message", e);
where e is the (Throwable) exception. Do not use the anti-pattern of:
log.debug(e);  // Do NOT do this
There is no Log.debug(Throwable) method, so the exception is cast to a string and the stack trace is lost.
  • Expensive log messages — If you're going to log a message that is computationally intensive to calculate, use the following pattern:
if (log.isDebugEnabled()) {
// expensive log message creation
String expensiveStep = webService.invoke("slowMethod");
log.debug("We should definitely wrap an " + expensiveStep);
}
// These also exist: log.isWarnEnabled(), log.isErrorEnabled

Web Application Development

User Interface

  • See openmrs display tag
  • Every openmrs business object needs to have a format tag for printing it to the screen.
    • These should be used wherever possible on jsp pages: <openmrs:formatPatient object="${patient}"/> is much preferrable to ${patient.name}
    • The tag should be called <openmrs:formatXXX ...> and live in src/web/org/openmrs/web/taglib as formatXXXTag.
    • Parameters that every tag should support:
      • object="${patient}" --> This is the object to be formatted
      • id="1234" --> This is the id of the object to be formatted, which will necessitate a call to Context.getYYYService().getXXX(id)
      • nolink="false|true" --> If false (the default) then the output might include a link, e.g. formatPatient might link to that patient's dashboard
      • size="compact|normal|tooltip|descriptive|bookkeeping"
    • To use a tag, you need to provide object or id (object should take precedence if both are non-null).
    • If a business object is voided or retired, it should be struck through: PIH ID 1234-1 Darius Jazayeri

Size examples

compact -> shortest reasonable representation, e.g. just the name of a patient or form, but the shortname of a concept
Darius Jazayeri
normal -> normal representation, and the default when size is not specified
PIH ID 1234-1 Darius Jazayeri
descriptive -> The normal representation, plus extra descriptive information, such as a form's description or a patient's age and gender
PIH ID 1234-1 Darius Jazayeri, 29 year old Male
tooltip -> Would look like the normal-sized version followed by an 'additional info' icon. That icon's tooltip would contain the extra description you'd otherwise see when size=descriptive
bookkeeping -> Like tooltip, but also includes all bookkeeping information (creator, date-created, modified-by, date-modified)
  • We should create tag files for standard data entry widgets, under <openmrs_tag:xxxWidget>
    • Parameters:
      • id & name for the form field element
      • size: popup | in-place
    • Create these for patient/user/person, concept, location, encounter type, etc.

SpringMVC

  • File naming:
    • user.list is the name of the page that lists users
    • user.form is the name of the page that lets you edit users
    • userList.jsp and userForm.jsp are the names of the actual files
    • UserFormController.java and UserListController.java are the names of the controllers
    • UserValidator.java is the validator
  • messages.properties
    • Object.property=label (eg Obs.patient=Select a Patient)
    • error.property=error description (eg error.gender=Invalid patient gender)
    • general.property=general label (eg general.description.label=Description)
    • formname.label=label (eg formentry.title=Form Entry)