I had chance to speak at yet another Lviv .NET UG meeting. This time I spoke about Domain-Driven Design. It looks like people got interested in this topic.
You can read my feedback post about this event at Lviv .NET UG site by the link “Зустріч #7. Враження, враження, враження…” (it is in Ukrainian).
I will write almost the same here and add some own opinions, so it would be interested not only for those who doesn’t know Ukrainian, but for those who would like to hear my personal opinions.
How it was
It was really great that we’ve got more than 80 people registered for this event. And if company where we hosted this event would provide us with bigger meeting room, we would probably have over 50 near 60 people attending. So as you understand because there was lack of physical room some people just turned around and went home. Crap! I would love to have them all listening to me.
Event has two speakers, me and Igor Racyborynsky. Ihor talked about “Getting Scrum”. We all played scrum and formed following product backlog:
It was really interesting, at least not as usual presentation. But he has not covered all about the scrum and at least key concepts. Although it went extremely good.
I talked about Domain-Driven Design for the 5th time. Yeah! I even don’t need any time to get prepared.
It was late in the evening, so many people got tired, especially me talking over 1 hour bombarding with new and new terms and mentioning lot of information about known concepts and patterns.
This time I’ve got many different cool presents:
Do you know what I like about delivering presentation to huge audience? – I always get couple of people that are excited about my speech and interested in my. They then come and talk with me as I’m an expert. I feet bit scary and nervous to show that I’m not that cool :)
As usual we have small beer party in the end. This time it probably wasn’t that small. You decide:
Speaking at JUG about DDD
Today I was invited to Java User Group meeting to talk about DDD again. Thank you guys.
Tuesday, Presentation for Main Office
Whole night before presentation I spent reading some awesome articles on DDD and really enjoyed it. That leaded to 4 hours of sleep. Also I took some anti-cold medicine with paracetamol, like on the picture below:
Rule of the presenter, you probably have never heard about:
Feedback that I got from this office is like 4.5 out of 5, in details my knowledge of topic and presenting was ok, but something was not really up to their expectations.
Wednesday, Presentation for Dev Centers
Did not get official feedback from them. None voted :(
Thursday, Presentation for Lviv2 Office
Lets start with the following question: Where is a car on the left or on the right?
You would probably say that Car is on the Right, and you are correct. But hold on. What is the difference between them? Difference is that Car is not just a huge collection of parts. Car is not even a collection of parts which need to fit together, but a car is much more than that. A good Car starts with vision and carefully written specifications, and it continues with design and with testing. The design is modified based on the testing results. Only after that each part could be assembled together.
Software development is similar, we cannot just sit down and type code.
What is Domain-Driven Design?
We all want to develop our applications, but business requirements nowadays are huge, and we dive in that complexity. And we can get stuck will all of that, because the whole complexity is in the Domain. For example banking system. Do you as a software developer want to know how does it work? Probably no, but you still need to develop your system. The guy from bank stands near you and and he wants you to develop application for him. What is your starting point? Is it table in database or is it Model?
Domain-Driven Design (DDD) is is an approach to the design of software, based on the two premises:
* that complex domain designs should be based on a model, and
* that for most software projects, the primary focus should be on the domain and domain logic (as opposed to the particular technology used to implement the system).
In other words the heart of the DDD is Model and you start developing your application with drawing your model. Model and design you create should shape each other. Model should represent knowledge to the business and it is language your team speak.
To be able effectively design you should build your Model and implementation together in the same time, cultivating a language based on the Model. You should represent knowledge in your Model and continuously distill it. You could do refactoring to it doing brainstorming and experimenting.
So, imagine that guy from bank, say Domain Expert, stands near you and you start talking. How does that conversion look like? It could be fractured and this creates serious problems to your project. Domain Experts have their own jargon and the development team has its own. After that daily discussions are disconnected from the terminology embedded into code. So effect of need to translate what you are talking about into code appears, plus misunderstandings possibilities. This all could lead to not good results.
Need for some common language shows up. Eric Evans calls this language as Ubiquitous Language. Your language is your model, this means that both Domain Experts and Developers should talk the same language which is build on the model. And changes to the Ubiquitous Language means changes to the Model. If Experts start using new terms this should be represented in your model, in your code, diagrams and speech.
How could it look?
* Speech (This means daily conversations between team members.)
* Diagrams (Often these are quick diagrams on the whiteboard, which you created with your Experts.)
* Writing (This could be NOT long documents, describing the Model.)
* UML (But there is no place for cumbersome one.)
Let us move to the way you will build your application.
I hope that everybody heard about the Layered Architecture. For the DDD it is very important since we explicitly separate Domain Level, which lives between our Infrastructure and Application layers, like on picture. Note, that isolating of your Domain Level is very important.
User Interface (Presentation Layer)
Responsible for presenting information to the user and interpreting user commands.
This is a thin layer which coordinates the application activity. It does not contain business logic. It does not hold the state of the business objects, but it can hold the state of an application task progress.
This layer contains information about the domain. This is the heart of the business software. The state of business objects is held here. Persistence of the business objects and possibly their state is delegated to the infrastructure layer.
This layer acts as a supporting library for all the other layers. It provides communication between layers, implements persistence for business objects, contains supporting libraries for the user interface layer, etc.
For example, a typical, the interaction of the application, domain and infrastructure could look like this. The user wants to book a flights route, and asks an application service in the application layer to do so. The application tier fetches the relevant domain objects from the infrastructure and invokes relevant methods on them, e g to check security margins to other already booked flights. Once the domain objects have made all checks and updated their status to “decided”, the application service persists the objects to the infrastructure.
The Smart UI “Anti-Pattern”
All this sounds good but simplicity of just calling infrastructure lead into temptation, and after that you see that code generated on button press actually executes hard-coded SQL, less terrible it could be when that is masked under some “helper” classes. But that all is manifestation of the the Smart UI “Anti-Pattern”. Could be surprising to you, but it has its advantages/disadvantages. Why not. It will not be a “Anti-Pattern” if it has no advantages.
Looks like a lot of advantages, but let see what are disadvantages. Hope this will convince you to do not use this “Anti-Pattern”.
So, don’t lose your mind, if you build your application with layers, do it correctly!
Building blocks of Domain-Driven Design
Eric Evans introduces terminology to the Domain-Driven Design. Accordingly to him there are such building blocks of the DDD: Associations, Entities, Value Objects, Services, Modules (which are parts of the Model) and Aggregates, Repositories, Factories (which deal with Life Cycle of a Domain Object).We will talk about each of the elements.
If your Car has four tires this is association of the model of your Car. This is relation between business elements you work with.
If we were to implement the concept of a Person using a software program, we would probably create a Person class with a series of attributes: name, date of birth, place of birth, etc. For example if you create Patient entity in code it will be represented like Patient class. It is required that for medical system to identify if the patient is the same. Is the patient “Andriy Buday” physician deals with, the same as “Andriy Buday” nurse has a scheduled procedure? So basically Entity is object that has Attributes and Identity.
Value Object are similar to the Entities expect that they do not need Identity, which is required for Entity. For example “Ukraine, Lviv” which is Address is Value object since we do not need identity for it. We could have 300 patients in Lviv (Entities) who could share the same address. This (sharing) could even be done thought the code using Flyweight design pattern to reduce memory needs. But Address is not always Value Object. This depends on “Who is asking?”. For the post system Address could be an Entity.
Consider that your Model has nouns and verbs (they are used in Ubiquitous Language). You could imagine that nouns are Entities and Value Objects. Your verbs are Services. If you have action or operation related to the Domain concept, but is not natural part of any of your Entity or Value Object you could move it into separate item, which is called Service. Interface of your Service should operate with terms of of the domain model. And the operations it does are stateless.
For a large and complex application, the model tends to grow bigger and bigger. The model reaches a point where it is hard to talk about as a whole, and understanding the relationships and interactions between different parts becomes difficult. For that reason, it is necessary to organize the model into modules. Modules are used as a method of organizing related concepts and tasks in order to reduce complexity. In code this could be packages or namespaces or even simple folders. The advantage of this is that when you place some classes together in a Module, you are telling the next developer who looks at your design to think about them together.
An Aggregate is a group of associated objects which are considered as one unit with regard to data changes. The Aggregate is demarcated by a boundary which separates the objects inside from those outside. Each Aggregate has one Root. The Root is an Entity, and it is the only object accessible from outside.
The picture shows that your Customer code could access Tire only though the Car instance and there is no other way to do that, since Car is Aggregate to Tire. But could be that part of Car need identity. This could be Engine of your Car, since it has its number and you need to form you taxes. In this case Engine is Entity which lives outside of your Aggregate.
We need to deal with Aggregates since they has direct relation to the Life Cycle of Domain Object.
Creation of an object can be a major operation in itself, but complex assembly operations do not fit the responsibility of the created objects. This operations could be accomplished with Factories. But not always it should be separate object. In scope of DDD Factory could even be a simple constructor. Two major requirements to the Factory are: Atomicity – this means that operation of creating an Entity should also create whole Aggregate associated with it. Abstracted to the type desired – this means that Factories should return you types rater than concrete classes. (i.e. Interfaces).
Repository is an object which provide you CRUD operations to your Aggregates.
Repositories have many advantages, including the following:
To be more clear how the Client code, Repository and Factory are interacting between each other. Lets take a look on the next sequence diagram:
I hope that diagram is self-descriptive.
Refactoring Toward Deeper Insight
Since the business and its rules are very complicated you are not able to see whole picture at once. If there are some incomes to your Ubiquitous Language you should represent this into your Model. You do this with refactoring of existing code in way that brings key concepts into light.
There are three ways to bring concepts into light:
Your development process should always be in continuous refactoring. Any changes to the matter you talk with your Experts should be immediately represented in code. If you see that some term starts occur too often just create an Entity or Service for it – bring it to the Light.
Preserving Model Integrity
Imagine a big enterprise project. Could you imagine it with one model which is understandable for whole team, say 200 people? Most commonly such big projects are divided to smaller teams and which of them works on own model. So whole project consists of couple of models and some of them are intersected with other models, some of them uses other models and all of this is the Integrity of your Models.
Each model has a context. When we deal with single model, the context is implicit. There is no need to define it. When we create an application which is supposed to interact with other software, for example a legacy application, it is clear that old application has its own model and context, and they are separated from the legacy model and its context. They cannot be combined, mixed or confused. When we work on enterprise project we need define the context of each model, we create. Also a model should be small enough to be assigned to one team. Only this way we could keep knowledge up to date and integrated into system.
Whole project should be assembled continuously using Continuous Integration.
Iterations between different contexts
You could probably say that you cannot have 100 models in your project without intersections between them. And you are right. Eric Evans sees few variants of such intersections.
The purpose of shared kernel is to reduce duplication, but still keep two separate contexts. Designate some subset of the domain model that the two teams agree to share. Of course this includes, along with this subset of the model, the subset of code or of the database design associated with that part of the model. This explicitly shared stuff has special status, and shouldn’t be changed without consultation with the other team.
There are times when two subsystems have a special relationship: one depends a lot on the other. You could imagine core functionality system and reporting system. Probably reporting system depends on the core one. In such case it is very and very important that team which works on core will be supplying reporting team. That is simply needed.
This is much like the Shared Kernel, but there is an important difference. The customer team cannot make changes to the kernel. They can only use it as part of their model, and they can build on the existing code provided.
We should build an Anticorruption Layer which stands between our client model and the external one. This layer works as a two way translator between two domains and languages. This layer could be needed when we deal with legacy/external model and have no ability to do changes to it.
Open Host Service
The solution is to see the external subsystem as a provider of services. If we can wrap a set of Services around it, then all the other subsystems will access these Services, and we won’t need any translation layer. The difficulty is that each subsystem may need to interact in a specific way with the external subsystem, and to create a coherent set of Services may be problematic.
One of the key requirements to your Infrastructure to be able deal with DDD is Persistence Ignorance. This means that once you have your Infrastructure setuped you no longer need to care about how your data goes to database and how it is saved there. Also it is commonly used with Unit Of Work Pattern. This could be accomplished with some of the ORM frameworks like Hibernate.
How do we classify ORMs? We look on the ways how it solves different problems which occur when you map you object-oriented data to relational database, but yea.. there are ORMs which map to the Object databases. But the most common variant is when you have Relational database. So wee take a look how ORM resolves Inheritance issues, what is the querying language of it, does it support all common databases operations like aggregating, ordering, grouping, etc.
Basically all ORMs has Identity Map and Cache strategy they allow us work using Unit Of Work pattern and they could provide Lazy Load to do not hit database often.
NHibernate is the ORM which all the features required to quickly build an advanced persistence layer in your code.
Patterns and practices compatible with Domain-Driven Design
Nowadays Agile is a way to develop software and DDD is very comfortable with it and with technics of the Agile like TDD.
Test-Driven Design is very good thing to use with DDD. Why? Because writing test before means that your are describing your model just like it was described on the meeting with your Experts. There are even enthusiasts who wants to rename TDD to BDD (Behavioral-Driven Design).
Advantages for design
While designing your architecture with DDD you often uses such patterns or approaches like:
SOA (Service-oriented architecture)
Generally speaking, SOA is a flexible set of design principles used during the phases of systems development and integration. It provides set of Services with which you are working.
AOP (Aspect-oriented programming)
In computing, aspect-oriented programming (AOP) is a programming paradigm in which secondary or supporting functions are isolated from the main program’s business logic. It aims to increase modularity by allowing the separation of cross-cutting concerns, forming a basis for aspect-oriented software development.
IoC (Inversion of Control and Dependency Injection)
Inversion of Control is principle with which you could easily resolve your dependencies in code delegating setup to some other part like IoC framework (StructureMap for example).
For the end I want to mention that my story is based on books by the link below.
I read all of them (except of duplicated ones :) ) and recommend you to read them also. This will bring a lot of interested stuff to your mind. For example I could state that now I understand better the design of subsystem with which I work on enterprise project. Future is for Domain-Driven Design.
P.S. I hope that you enjoyed my story. Do not hesitate to leave your comments.