In the training courses I've given, providing concrete examples (not just UML diagrams) help the course participants understand better. Here in this post, I'm sharing a Java-implementation of the revenue recognition problem (as described in Martin Fowler's PoEAA book). The problem is used to explain three domain logic patterns: transaction script, table module, and domain model.
Revenue Recognition Problem
Just in case you haven't read the book, here's an excerpt from the book that explains the revenue recognition problem, taken from Martin Fowler's PoEAA book (page 112).
Revenue recognition is a common problem in business systems. It's all about when you can actually count the money you receive on your books. If I sell you a cup of coffee, it's a simple matter: I give you the coffee, I take your money, and I count the money to the books that nanosecond. For many things it gets complicated, however. Say you pay me a retainer to be available that year. Even if you pay me some ridiculous fee today, I may not be able to put it on my books right away because the service is to be performed over the course of a year. One approach might be to count only one-twelfth (1/12) of that fee for each month in the year, since you might pull out of the contract after a month when you realize that writing has atrophied my programming skills.
The rules for revenue recognition are many, various, and volatile. Some are set by regulation, some by professional standards, and some by company policy. Revenue tracking ends up being quite a complex problem.
I don't fancy delving into the complexity right now, so instead we'll imagine a company that sells three (3) kinds of products: word processors, databases, and spreadsheets. According to the rules, when you sign a contract for a word processor, you can book all the revenue right away. If it's a spreadsheet, you can book one-third (1/3) today, one-third in sixty (60) days, and one-third in ninety (90) days. If it's a database, you can book one-third today, one-third in thirty (30) days, and one-third in sixty (60) days. There's no basis for these rules other than my own fevered imagination. I'm told that the real rules are equally rational.
Emphasis, formatting, and numbers added for clarity
Domain Logic Patterns Example (Java-implementation)
The working code is available at my GitHub account.
I used the same integration test (AbstractRevenueRecognitionServiceFacadeTests) and applied it against three different implementations: transaction script, table module, and domain model.
Here are the three (3) patterns:
- transaction script — organizes business logic by procedures where each procedure handles a single request from the presentation.
- domain model — an object model of the domain that incorporates both behavior and data.
- table module — a single instance that handles the business logic for all rows in a database table or view.
For transaction script and table module implementations, I used table data gateways.
For domain model, I used data mapper (via JPA/Hibernate). I tried to stick as close as possible to the database schema provided. In fact, all three patterns work with the same database schema.
The transaction script implementation can be found under the revenue.recognition.transaction.script
package. The table module implementation is under the revenue.recognition.table.module
package. The domain model is found under the following packages: revenue.recognition.interfaces.facade.internal
, revenue.recognition.application.impl
, and revenue.recognition.domain.model
.
Please feel free to review the implementation, and let me know of any items I've missed.
Note that having three (3) different patterns in one application is unnecessary (and quite confusing). I've only done this to better compare the different approaches to organizing domain logic.