This post is a high level description of the architecture of Kill Bill as a billing system. The intent is to describe briefly the various core components and how they interact with one another. The diagram below shows the main core services in Kill Bill:
- At the bottom of the stack, we have the catalog service, whose role is to provide the catalog information that is pertinent to a given tenant. The catalog service offers an API to retrieve product information, alignment rules, prices,..
- The entitlement service offers an API for managing all the entitlement information associated to a given subscription — its state (started, paused, resumed, stopped,…), its ownership, the various dates where state transition,… The entitlement system offers a different view than what is reflected in the invoices to allow fine tuning of what should be billed versus how the service is provided. In addition to that API, it will generate (persistent) bus events that other services can register to.
- The invoice service offers an API to retrieve past invoices, make any adjustments, or even trigger future invoices. In addition to that API, it will generate bus events that other services can register to.
- The payment system offers an API to retrieve past payment/refund information or to trigger new payments/refunds. In addition to that API, it will also generate bus events.
- The overdue system is a generic system that is driven through a configuration, to take action when payments are failing and users end up not paying. A complex set of phases can be implemented to gradually terminate the service, notify the users, … if needed. It also offers an API and generates bus events.
In order to avoid services calling each other in a loop, having a long chain of API calls across services, and also managing dependencies between services, cross service API calls are only allowed from the top to the bottom of the stack; so for instance the payment service can make API calls to the invoice system but not the reverse. However, each service can register to receive bus events from other services and so communication from invoice to payment is made possible that way. The persistent nature of the bus events guarantees the delivery and the asynchronous aspect makes sure that there is not a long chain of calls that could happen synchronously.
The diagram above is not an exact representation of how the services have been implemented but constitutes a logical view of these components. In reality some of these services are implemented using multiple sub services; for instance to keep track of generic system events, or to comply with the flow of API calls described earlier.
Also, in addition to these core services, there are many more other pieces of interest:
- Audit and history: Each API call that is changing the state of the system will be audited, and its previous state will be kept in a set of historical tables. We built in our DAO layers a mechanism to intercept each database call that would modify the system so that it can insert transparently the audit and historical rows within the same transaction–to ensure atomicity of the operation.
- JAXRS layer that is offering a REST APIs on top of these core services. So while Kill Bill could be used as an embedded library, most users will probably want to use it as a web application.
- International support and template system.
- RBAC integration.
In a subsequent post we ‘ll describe Kill Bill as billing platform… which opens the doors for building a more complete stack.