One thing I’ve been interested in recently is Domain Driven Design – I’ve not really read into it in depth but from conversations with friends and colleagues I feel I’ve understood at least the high level concepts.
Essentially, the idea revolves around segregating code into 3 layers:
This contains the code that orchestrates the application from a very high level. In a system, the application layer should tell you all the different things that the code does. For example, in a Library system, the application layer would contain classes such as CheckoutBook, SearchForBook, etc. The application layer will call into the domain layer, which tells us how the system should do these things.
This layer describes the different concepts the system needs. It contains all the business logic for the system. It is the code that is least likely to change, as business rules rarely do. The classes in this layer of a Library system would be things like Book, Index, Member, etc. The domain layer should not depend on anything outside of it, but many things will depend on it.
This layer purely looks at the adapters in and out of the core domain. This is the stuff that can change frequently (and should be able to change easily), and so nothing should depend on it! However, it will depend on the rest of the application. For a library system, the infrastructure layeer would be the objects that connect to the database, or the classes which take data from the user, e.g. from a form on the website.
Applying the above
In my very simple project, I used this structure to organise my code. This now gives me a context to refer back to when I read into DDD more deeply so I’ll understand it a little better.
The application layer contains one class – a ShoppingListGenerator. This class takes in a MealPlannerCreator and a Destination and essentially asks the MealPlan to create a list for itself and output it to the Destination. The MealPlannerCreator and Destination are interfaces, which meant that I could assume their behaviour (the MealPlannerCreator creates a meal plan, and the Destination outputs the shopping list), but I postponed the decision of how that would happen, or the implementation of the interfaces, until much later.
The domain layer contains two classes:
POJO simply housing a list of ingredients and instructions for how to make the meal
a simple list of Meals, and responsible for creating the shopping list for itself
With the domain classes, my focus was thinking about where the behaviour/logic should live. Should the Meal add itself to a MealPlan, or should the MealPlan add meals to itself? In fact, I initially had a separate ShoppingList class which took in the MealPlan and output a shopping list, but when I thought about it further I realised that extra class added no value. The MealPlan knew about its meals, why wouldn’t it make sense for it to just generate its own Shopping List?
The infrastructure layer contains the implementations of the two interfaces I mentioned in the application layer, and a Runner which will kick off the entire process. At the moment, I have a directory which represents my meal plan, and drop recipe files into it. These need to be in a certain format. The infrastructure layer of my project reads these files in, and converts them to a Meal domain object, and adds them to the MealPlanner. It also just prints the final shopping list to StdOut. This is pretty simplistic, so depending where I want to take this app and what the different use cases are, I can change these. I may want to send the ShoppingList straight to the supermarket website, for example. Having the interface in place makes switching out the implementations easier, if we do want to send the shopping list elsewhere, then only the implementation needs to change, and we’d pass this new implementation into the Runner.
I made each of these layers its own separate subproject, so that each could be deployed separately. Obviously, with such a small project it matters very little, but in a larger project this flexibility is invaluable – deployment is less risky and can be done much more frequently.