Embarking on a new application is a great time to push the boundaries of what we are comfortable with. Whilst a few chapters into Eric Evan’s book, Domain Driven Design, my team and I started defining where our classes will live inside the new project.
As a result I’ve designed a question flowchart as a tool for reference when it is not immediately obvious where a class should go. If the class in question fits into multiple locations, it is an indicator that the class is doing too much and not following the Single Responsibility Principle (SRP).
Here we have the concept of Clean Architecture, complete with labelled areas which map to the flow chart of questions above it.
The inner most layer is the Domain. This layer is manipulated by its surrounding layers. The Domain layer has no knowledge or visibility “outwards”. Its purpose is to represent core business concepts.
If the answer to this question is Yes. It is a Domain object, representing a core concept defined by the business. This is assuming that the class satisfies no other question which points to another package: Application, UI or Infrastructure, which we’ll cover shortly.
A quick identification of a domain object is one which is discussed with analysts, support, etc.
Lets take the example of an internal mailing system, where employees of a company have In and Out trays. The trays would be defined with the aid of an analyst or other expert, their purpose, their capacity and so on. A clear domain concept, therefore the Tray class or classes resides in the Domain layer and therefore the Domain package.
Let’s look at logging as another example. Asking ourselves the same question, logging who sent a message to who, and when, would be talked over with a support team monitoring these interactions. The Logging class or classes which handle this concept (not logging’s specific implementation or presentation but an interface or similar abstraction) should be placed in the Domain package. The term “logging” is used when discussing with support or anyone other than a dev on the team.
When asking ourselves this question we don’t have to exclude the developers on the team. When discussing a domain object in terms of a use-case with fellow developers, this is equally valid when focusing on a business or analytical point of view.
In our internal mail system, Trays may be discussed. An employee may ask an analyst: “Where does the mail I want to sent go?“, the answer being: “In the Out Tray on your desk“. The first question would suffice to place this Tray class in the Domain. A follow-up question of: “What is a Tray?” could be answered by referring to the Tray class, which acts as “living” documentation. Referring to its usages shows its flow through the use cases.
A more established company, say one which has been around since the days of physical internal mail (I have been told this is how people really communicated), would have the idea of trays ingrained already. It would be fair for a developer to decide that the above conversation would not occur, except maybe if a new team member joined. Perhaps a youngster unaware of how mail once worked. Here comes to play the second question, which maps to the Domain layer also.
Our mail example was once done manually and still is, though its use has somewhat changed. Other use cases may be more difficult to imagine being done manually. If in doubt, if you could drive a car, ride a horse or walk to a different building and provide a piece of information on paper, then the information itself would be a domain object (not what it is written on). Here we have a request.
For the online services I use, I would expect someone, at some point during the development of the service to have discussed what the log-in process looks like, the parameters the users must provide. In this example the log-in request is the domain object, whilst the process of logging-in is a use case. Which leads us on to the next set of questions in the flow chart which direct us to the Application package.
So our class isn’t a domain object, we answered No to both questions above. It doesn’t feature in the centre of our architecture, so it may have a place one layer outwards with visibility inwards of the domain. In other words, our classes responsibility may be to manipulate Domain objects, to describe the flow of one of our domain objects, how it is transitioned or modified. An example of this would be what a mail clerk, who’s role is to take mail from an Out Tray to and deliver it to a specified In Tray. This is a use case.
So far this is the only definition I have defined as a class befitting the Application layer.
Like the Application package, the UI package has only one question leading to it.
The UI layer, having visibility of the Application layer, handles the presentation to anything external to our app. An example of a UI class would be the model of our mailing system’s envelopes. They may have different colours to signify importance. These coloured envelopes have significance to users, however the process of delivery could function with plain envelopes.
Now we move onto the Infrastructure package.
The Infrastructure layer, in my experience tends to grow quite quickly, holding specific implementations of interfaces placed in inner-more layers. To aid discoverability of classes I find a good approach is to mimic the package structure of the Domain, Application and UI within the Infrastructure package. Akin to how a test package mimics the src. Do this if you feel it is worthwhile.
This class will have answered “No” to all previous questions, provided is follows SRP. Answering “Yes” to this question indicates that this class communicates with the outside world, it may read/write to files or a database, or communicate with other apps or the internet via http. In a Clean Architecture world the infrastructure surrounds the Domain, Application and UI layers. With visibility inwards. An example of an I/O class in our mailing example would be requests for more envelope stock to a application which handles office inventory.
All libraries should have as little impact on the codebase in the event of an upgrade, replacement or removal. Separating these reliances from the inner layers prevents pain in handling changes to these third party dependencies. How much of your codebase would be affected if your favourite library was deprecated in favour or a new or different implementation? Or a bug was found? Following this project structure results in only the Infrastructure layer being affected. The Application layer would still manipulate the same Domain layer objects, with no knowledge of the infrastructure package surrounding it having changed.. The only difference being the business functionality defined in the Application would be utilised using a shiny new, bug-free (we hope) library.
In our mailing example the mail clerk who picks up and delivers mail may keep an audit of who delivers to who. The format and data inside an audit record would be a Domain object. Why? I can picture a Business Analyst reminding me to store the date and time down to milliseconds in the AuditRecord class, as the business expects this functionality to be used very frequently. I’ve just used AuditRecord in a discussion with someone other than a dev on the team. This places the AuditRecord class in the Domain layer.
The process of taking a piece of mail from a Tray and delivering it to another Tray is defined in a use case. Flowing my class through the diagram I find that this class goes in the Application layer.
It is suggested that the audit records be written to a message queue instead of a database. No problem. Since I’m talking to an external service this code is in the Infrastructure layer. Changing the way I store my records has no affect on the rest of the functionality, as the use case in the Application layer makes calls to the underlying interface for persisting these records. The audit records are constructed, formatted and move through the use cases as they always did.
Below is the example of mail delivery as a java project. Showing what the package structure would look like. Subject to change as the project would develop.
Let me know what you think, try the flowchart out on your own project. If you have any suggestion or additions I’d love to hear them.