While several implementations have been provided during the last decades, how to structure and organize the model is still open to debate and will be at the core of this article.
Randy Stafford distinguishes between Domain logic and Application logic in the book Patterns of Enterprise Application Architecture.
Domain logic is described by all the business rules that govern the domain in use. In the case of a football league, the domain logic involves teams, players, matches and all the rules that inter-connect them.
Application logic contains all the rest. It is the logic that would not exist outside of the application. It is not specific to the domain but it is required by the application in order to fulfill its requirements. Some examples are exporting data to CSV, sending emails, and persisting data to the database.
The last remark is probably the most difficult to digest, but it is also the most important. The database is not part of the domain logic.
Going back to the football league example, the domain logic does not involve databases, tables and columns at all. The database exist only because the application needs it (maybe).
The database is just a way to store data, it is a detail. In the same way the HTTP is a detail. The HTTP is just the protocol the application uses to communicate with the external world. Same goes for CSV files, FTP servers, RMI, REST and so on.
They are all details. They are always for the application to interact with the outside environment. If you migrate from MySQL to MongoDB or Riak your domain does not change at all. What changes is something specific to the application, which is the way for the data to persist.
Business logic in the J2EE worldIn the Java/J2EE world, the domain model is usually made of beans. These beans are mapped to database tables using Hibernate or equivalent. A bean is an object with an empty constructor, a set of private fields and a getter/setter pair of methods for each field. However, a bean does not contain any logic, it is a mere data structure.
On top of the beans there is the service layer. The service layer consist of a set of POJO's carrying all business logic. Services contains both domain and application logic. Hopefully, those handling domain logic are separated from the ones taking care of the application logic.
Usually, on top of these service POJO's there are service facades. A service facade set up a context for inter-services collaboration. It also takes care of cross-cutting concerns like transaction management, security and so on.
The whole point of Object Oriented Programming is to encapsulate and bundle together data with behavior.
This means that a standard J2EE setup violates this principle since it separates data (beans) from behavior (services).
Business logic in the Rails worldThe Rails approach is simply the opposite in that models contain both data and logic, which is great.
This approach is well known in the Rails community as
skinny controllers and fat models.
The problem is that models do not contain just domain logic. All application logic is to be found in them as well. This includes database mapping, callbacks and even controller specific concerns, like the
This approach can lead to serious problems. As the business logic grows and grows, models end up being obese!
The Ruby community has faced the problem and has started looking for solutions. The most popular at the moment seems to be DCI, which stands for Data, Context and Interaction.
DCI can seem to be very elegant at first but it has his drawbacks. Many mention implications on performance. Others the fact that DCI can require a lot of boilerplate especially when the number of use cases is considerable.
In my opinion the problem with DCI is exactly the same of J2EE. Data and behavior are separated.
The model acts as data structure at the same way as java beans do. Roles hold logic but no data, same as Java services. The Context bundles together data and roles. Eventually, it adds cross cutting concerns like transaction management and security as well. Does it smell like J2EE service facades?
Separating the domain from the application logicBoth J2EE and Rails approach can work quite well, even though they both have pitfalls. But as soon as the number of use cases increases, they both don't scale very well and code will start to smell.
In those cases the simplest way to go is to just separate the domain model from the application model.
The domain model has to contain nothing else but domain logic. It does not have to persist anything to the database, it does not even have to know that a database exists!
The domain objects will bundle data and behavior together. This provides better information hiding, encapsulation and modularity: the three most important concepts in software engineering.
Services can still exist in the domain model. They will contain operations involving more than one domain object and that do not really belong to any of them.
The domain will be packaged and distributed. It can be a JAR file, a Ruby gem, it does not matter!
The same domain module can obviously be imported by several distinct applications, increasing re-usability and maintainability enormously.
The application model imports and uses the domain model. It also takes care of the interaction with the database, CSV exports, requests to external services and so on.
It does not matter whether you are working with a Java application, a Ruby application, an enterprise application or even a desktop one. What matters is that you solve a certain problem for a specific domain. Your focus has to be the domain you are working with. Forget about databases, HTTP, REST and company. Those are just details. You can change them whenever you want, but the domain will remain the same.
I got inspiration for this article from the talk of "Uncle" Bob Martin during the last Ruby Midwest Conference.
Of course this approach requires much more initial work and it could be unworthy for simple systems. However, as soon as the system grows, the time and the pain saved will be considerable.