Evolving the Mifos Code Base

Plans and Guidelines for Evolving the Mifos Architecture

This page discusses a proposed approach for evolving the current code base. Three interrelated goals are discussed here:

  1. making Mifos more modular
  2. making Mifos cleanly layered
  3. defining APIs in each module for internal use in Mifos as communication points between modules and to expose as integration points for external applications or plugins.

Some of the ideas here were tested out in the "Cheetah" version of Mifos, which was done as an exploration of what we would like the structure of Mifos to ultimately look like.

Groundwork

The following steps were requirements for proceeding with the work proposed here. Each of these has now been completed:

  • Get sufficient acceptance test coverage in place on Gazelle to make major changes with confidence.
  • Get build process to use the Maven repository for library management
  • Make all unit and integration test classes run independently of one another.

Application Module Decoupling

At the maven module level, Mifos is currently divided into the following modules:

  • acceptanceTests
  • application
  • common
  • serviceInterfaces
  • testFramework
  • userInterface

The acceptanceTests module encapsulates all our SeleniumRC based acceptance tests and makes use of some more generic functionality provided by the testFramework module.

The serviceInterfaces and userInterface modules were included to mirror the structure that was used for the "Cheetah" (proof of concept) version of Mifos.

The common module includes generic support used in both tests and the Mifos application code.

Finally, the application module contains almost all of the code in Mifos. To make it faster and easier to develop Mifos, we want to split the application module into multiple modules where each module contains code that deals with a well encapsulated functional area of Mifos. Interactions between modules would be support by making calls into the API of a module.

The current structure of Mifos divides the application at the package level inside the "application" module as follows:

  • org.mifos
    • application
    • acceptedpaymenttype
    • accounts
    • admin
    • collectionsheet
    • customer
    • fees...
    • framework
      • business
      • components
      • exceptions...

If we modularized Mifos along the lines of functional areas, then we could imagine a package structure (with corresponding modules per top level package) that looked something like this:

  • org.mifos
    • accounts (including loans and savings)
    • users
    • customers
    • surveys
      • ppi
    • reports...

With a structure like this, we could could strive to each module self contained by having all interaction with the module be through an API defined for the module. In this way, each module would operate only on the business objects defined within that module and would then use a service level API (and associated data transfer objects or DTOs) to interact with other modules.

Some goals of moving to this kind of modularization are:

  • make it easier for someone to contribute to Mifos. By reducing the amount of Mifos code someone needs understand in order to contribute effectively to the code contained in a single the module it should be faster and easier for someone to come up to speed.
  • make it easier to maintain Mifos. Breaking the code down into modules will create a more loosely coupled application. By reducing the tight coupling between domain objects, Mifos should be easier to test, easier to add functionality to, and the likelihood of bugs being introduced with code changes should go down.
  • once the module is separated out, it makes it easier to safely make significant changes like major refactoring or rewriting of that area of the application.

Incrementally increasing modularity

A big question about increasing modularity is "How do we make Mifos more modular in an incremental way?"

One approach would be to start rearranging package structure by moving functionality out of the "application" module to match our target package (and module) structure, and begin introducing APIs for the top level packages. Work could to eliminate dependencies on business objects from other high level packages could be done incrementally until reaching the point that all "foreign" business object references have been removed and either replaced by API calls to another module or redesigned away. At this point the now independent package could be made into a maven module that would build a jar file.

Here's a sketch of what an initial step towards modularization might look like by pulling out high level packages for accounts, customers, reports and userInterface. The idea is to reorganize the package structure first, to match what we expect the module structure to eventually be. As part of this process, we can reorganize packages so that under org.mifos we will have packages split by functional area of the application. Much of the code currently under the "framework" package is actually specific to a given area of the application, so it could be rolled up under the application package and then have subpackages distributed to the functional area where they belong. Eventually these intial high level packages will be broken down further once enough of the tight coupling within them has been removed.

  • org.mifos * accounts (including loans and savings)
    • acceptedpaymenttype
    • api (this is where API interfaces and DTOs would be defined)
    • fees
    • loan
    • productdefinition
    • productsmix
    • savings...
    • customers _ center _ checklist * client...
    • reports _ admindocuments _ branchreport * cashconfirmationreport
    • userInterface
      • struts
      • accounts
        • acceptedpaymenttype
        • fees
        • loan...
      • customers
        • center
        • checklist
        • client...
    • application (remaining packages that have not yet been separated out)
    • framework (remaining packages that have not yet been separated out)

Right now each of the packages under org.mifos.application also include user interface classes. The plan here is to pull these classes out into a separate org.mifos.userInterface package. These should be separated from the business logic and use an API (or service facade) to call down into the domain (business) code. Before the UI classes can be separated out, the presentation logic needs to be decoupled from the business logic and business objects.

Improving the Layering of the Mifos Architecture

Currently the presentation layer of Mifos (Struts action classes) has business logic mixed in and directly references business objects (and in some cases persistence code).

There are also persistence operations that are mixed into some business objects. We would like persistence (and transactional) operations to be cleanly separated from business objects in order for business objects to be true POJOs (plain old java objects).

In order to move to a more maintainable code base and to facilitate breaking Mifos into modules, we would like to move to a cleanly layered architecture.

In order to do this we want to:

  • push business logic down and out of the struts action classes and encapsulate it at or below the service layer
  • push business objects out of struts/presentation code and only use data transfer objects (DTOs) returned by or passed in to service level API calls.
  • define service level APIs for presentation code to make use of.
  • cleanly encapsulate persistence (CRUD, create-read-update-delete) calls in Data Access Object/Repository classes (currently there are "xxxPersistence" classes that are along the the lines of a DAO).

Once the presentation layer has been cleanly separated from the business logic and objects, then we can take steps like:

  • move presentation code to a separate module
  • reimplement the UI using a current framework like Spring MVC

Making all business object into POJOs will mean that code can be more easily unit tested in memory.

Defining APIs for Mifos

APIs play a key role in moving Mifos to a loosely coupled, easier to understand, maintain and extend architecture. They will be used:

  • for the presentation layer to access Mifos functionality
  • for one module to access functionality in another module
  • to facilitate integration with external functionality (transaction import, mobile banking integration, Mifos light integration)

One proposal is to add an API package below each highest level module package. This API package would contain all the API methods and DTO classes for moving data in and out of the API calls. The eventual structure would then look like:

  • org.mifos * accounts
    • api
    • users * api
    • customers * api
    • surveys_ppi * api

For any given module, a developer would know where to look for the external API to that module.

We need to compile a set of guidelines as to what rules we would like our external APIs to follow. Here is an initial list:

  • an api should not expose any business objects (persisted entities) outside the api.
  • all return values should be based on built in java types or data transfer objects that combine data but do not implement any behavior.

Specific Mifos Layering, Modularization and API Projects