This post is the second of a set of post on the same topic starting here.
Where do I put business logic?
I’ve seen this question raised a lot of times by developers who are dealing with management applications using a persistence stack. This question is simple and legitimate but the answer is a little bit trickier.
First statement: there are different kinds of business logic which have to be in different layers. Business logic is itself fuzzy and quit large. To some extreme some could argue that the entities definition is part of the business logic, even the primary keys as a way to uniquely retrieve an entity. To the other end, a business process is considered as a kind of business logic. I personally think the business logic is wide but exclude the example I just mentioned.
Even if an entity has obviously some business inside its definition, its only data, so no logic at all. For the primary keys, I consider that a exposing primary keys is a mistake that prevent evolution and prevent implementation of high level technical services as archiving for example. The best is to expose 2 attributes per entity for retrieval: one which is human readable (as the ID of an employee) and another one which will be used for application integration. The idea is that these “IDs” are not varying from a system to another, or during the life cycle of the entities (except for exclusive business reasons), in the contrary of an internal primary key which could vary over time.
Bitwise, you can consider that the mandatoryness of an attribute (or validation) are part of the business logic. I agree some could argue it’s exclusively a data modelling concern. That’s not totally wrong but I think there is a mismatch between engine location and configuration. The engine location is of course in the data modelling layer but as this is part of the business logic (from my point of view) this should be configured in the same area as the other business logic: in the configuration. I will address the problematic of configuration later on. At the same level you have other business logics as: right management, partitioning and entity validity (broader than attributes validity).
Back to the initial question: where do I put my business logic? IMHO, the best is to define it in a set of configuration files and let the entities delegate to the appropriate engine. This provides a flexible approach (separation between the configuration and the engine) which is also generic (usage of an engine). This question is not totally addressed by this approach and hides the real problematic: scope of business logic.
The scope of the business logic is far more meaningful and key for a persistence stack. There are at least 3 different scopes:
- entity attributes
- entity
- application
- inter-applications
The entity attribute scope is for example the scope for validation/mandatoryness of the attribute PONumber in a purchase order for example. The entity scope is for example the global amount of a purchase order which is supposed to be the sum of the amount of the purchase order lines. The application scope is supposed to address the internal workflows to the application as the approval workflow of a user request for example. The inter application scope will address business logic between applications as between the requesting application/service and the catalogue application/service.
You will notice the “entity” word is ambiguous: does a purchase order line is an entity or not? IMHO, from a strict business perspective, I don’t think so, even if it’s convenient. I think a DAO can be considered as entity as soon as it has it’s own validity in the business: does not make sense to talk about a purchase order line outside of the scope of a purchase order. This distinction is sometimes used for value objects versus DAO. I personally prefer use entity and DAO and have both of them in separate layers: Data Access Layer (or data modelling layer) and Domain Layer.