Domain-driven design is focused at the Domain Layer which is one of the common layers in an object-oriented system with a multi layered architecture. Complex domain designs should be based on a model. The domain model should form a common language for describing system requirements, that works equally well for the business users or sponsors and for the software developers. In Domain-driven design, we only need to concern about object oriented concepts. We capture domain elements and represent them in object oriented concepts like objects, methods, etc. The design needs to be both nice and usable as it is the foundation for rest of other stages of software life cycle. When designing, we need to think whether we can implement this design in the real world. We should be able to change the technology, if we couldn’t achieve the design with the existing technology. We should not keep two separate designs for analysis & design phase and for implementation. Design model and the implementation model should be aligned. There should be feed back loop through out the life cycle.
It is needed to avoid mismatches between domain experts and software developers. Multiple words can be used to represent same things and one word can be used to represent the two things. We should avoid those kinds of language ambiguities.
Model Driven Design is broken down into the following artifacts:
- Value Objects
Each one of these artifacts complements one another and allows us to gain greater insight into the business domain. Ubiquitous Language is the backbone or glue rather that holds all these artifacts together.
Services: Services are not part of the domain, but need to carry out the domain. Considering bank domain, transactions can be identified as domain services and providing pawning, and secure safe are some examples for infrastructure service. We can use services to capture responsibilities and system requirements which are not directly related to the domain. Class should have only one responsibility assigned to it.
Modules: In a complex system the model tends to grow bigger and bigger, so it is necessary to organize the model into modules. Therefore modules are used to avoid complexity. Divide and conquer is the method used to handle complexity. Dependencies between modules should be removed. Modules should be able to handle independently. Coupling should be less between modules. These modules also should reflect domain concepts like module naming.
Aggregate: An Aggregate is a group of associated objects which are considered as one unit with regard to data changes. Every aggregate has a boundary. It can be divided into normal aggregates and root aggregates. Root aggregate is the owner of other classes within the boundary. The root holds references to any of the aggregate objects, and the other objects can hold references to each other, but an outside object can hold references only to the root object.
Factories: Objects have a life cycle staring with creation, then retrieving, and ending with deletion. Factories can be used to create complex objects as hierarchies. When a Factory is not needed, we can use simple constructor to create objects. We have to identify correctly when factory is needed. Factories need to create new objects from scratch, or they are required to reconstitute objects which previously existed,
Domain Driven Design
- The idea of domain driven design is to capture all requirements in our model and can’t assign ad hoc responsibilities to domain classes. This comes with domain layer in layered architecture. Here we don’t concern infrastructure details and technology. We only need to concern about object oriented concepts. That means capture the concepts and represent those concepts in object oriented manner, like classes, objects etc… Normally these are commonly known things. However design should be nice and usable, because it becomes the foundation for rest of other process.
- If the existing design cannot be afforded by using existing technology, it should flexible to change technology to achieve the design. It should not keep two designs for analysis & design and implementation. That should be same. So it can map one to one between design model and implementation model. When it is using only one design there should be a feed back loop through out the life cycle
- Language can use different words to express same thing and can use one word to represent two things. These are wrong. Better way is to represent one word for one thing. Then we can reduce conflicts and provide more meaningful language.
- Services are not part of the domain, but need to carry out the domain. Actually services capture responsibilities and system requirements. One of the best practice is one class should have only one responsibility. As an example consider the university domain and its parts like library, canteen, bookshop, examination department. All of these are services and library, canteen, bookshop are infrastructure services. So university domain can exist without this domain. But the service like examination department is domain services, means these are mandatory service for university domain. It can’t exist without that domain services.
- We can use modules to reduce complexity. It’s very important to increase cohesion and reduce coupling. It’s important to remove dependencies between modules and provide the ability to work independently. There can be tight coupling within module and reduce the coupling among modules. As an example consider a DVD player and amplifier. Here there are many open jacks to connect other sub systems but no way to change interior of particular sub system. Also these modules should reflect domain concepts.
- Every aggregate has a boundary. It can be divided into normal aggregates and root aggregates. Root aggregate is the owner of other classes. In the example we are discussed customer is the root aggregate. Contact info and address can’t change without knowing the customer. That means customer has full control of these two.
- Object creation and assign something to object is two different things. As an example construct car and use car is two different things. Factories responsible for create objects. If we want to do a particular job we need to call objects. Repositories like directory. Those are help to find objects inside the memory. These repositories hide implementation details, means encapsulation is there. Here we don’t need to concern where the object is and only need to refer matching criteria.
S.R.S. Gunawardana - 044012
Domain Driven Design
Domain driven design comes in domain layer in the layered architecture. There we are not concerning about any particular technology or infrastructure, but object oriented concepts. We capture domain elements and represent them in object oriented concepts like objects, methods, etc. The design needs to be both nice and usable. The design which is nice but not usable is not worth and vice versa. When designing, we need to think whether we can implement this design in the real world. We should not keep two separate designs for analysis & design phase and for implementation. It should be the same design we should implement. Although there is a feedback loop through out the life cycle, the same design should continue through out the all stages of the software life cycle.
We should avoid mismatch between domain experts and software developers.
We should also avoid;
- Usage of multiple words to represent the same thing. For example debit & credit and withdraw & deposit to represent the same thing.
- Usage of one word to represent two things.
If we take university domain there we can identify several services which are related to that domain. For example canteen, library, bookshop, examination dept, etc. Canteen, bookshop can be given as examples for infrastructure service and examination, other departments are examples for domain services. We can use services to capture responsibilities and system requirements which are not directly related to the domain. Without services we assign that responsibility to some other class which is not good. Because a class should have only one responsibility assigned to that. We use artificial classes to assign those responsibilities as services.
To avoid complexity, we use modules. Divide and conquer is the method used to handle complexity. Since one person can’t comprehend the whole complexity, can use modules. We should remove dependencies between modules, so that they can be handled independently. Within sub system or modules, there exists high level of coupling. But between modules coupling is less. These modules also should reflect domain concepts like module naming, etc.
Aggregates and aggregate roots:
Also known as concurrency control and ownership. Every aggregate has a boundary. If we take a look at the figure above, can’t change contact info or address without knowing the customer. Customer is the owner of it. If two people are going to change this information, there would be concurrency issues. Customer is the one who have the control over the contact info and address.
Object life cycle:
Objects have a life cycle staring with creation, then retrieving, and ending with deletion. Use factories to create complex objects. Otherwise use a constructor. Consider previous figure; if all customers need to have contact information and how we are t enforce this rule? It’s not a customer class responsibility. So create a factory and assign that responsibility.
By: W.A.A.D. Wickramaarachchie 
During the last few lectures on Advance Software Engineering we talked about concepts such as layered architecture, entity objects and value objects etc which all come under the term which was coined by Eric Evans as Domain Driven Design. It is an approach for software design which is based on arguments such as;
1. For most software projects, the primary focus should be on the domain and domain logic. (Not driven by a particular technology)
2. Complex domain designs should be based on a model.(Making understandability high)
For a sophisticated real world software to be well satisfying to its clients, it should be domain driven rather technology driven which ultimately will address the heart of (the sole existence) software. The heart of software is its ability to solve domain-related problems for its user. All other features, vital though they may be, support this basic purpose (such as notification and statistical reporting systems etc as added functionality).
How can we make the software fit harmoniously with the domain? The best way to do it is to make software a reflection of the domain. Software needs to incorporate the core concepts and elements of the domain, and to precisely realize the relationships between them. Software has to model the domain. A domain is something of this world, a particular area of discipline or a sphere of knowledge, influence, or activity.
A model is a simplification. It is an interpretation of reality that abstracts the aspects relevant to solving the problem at hand and ignores extraneous detail. According to Eric Evans, a domain model is not a particular diagram or a document/specification; it is the idea that the diagram is intended to convey. It is not just the knowledge in a domain expert’s head; it is a rigorously organized and selective abstraction of that knowledge. This can be understandable among all parties involved in the requirement gathering to implementation and so forth.
When choosing a model we can consider the following principles,
• The model is distilled knowledge.
• The model is the backbone of a language used by all team members.
• The model and the heart of the design shape each other.
Ubiquitous has a meaning widespread, constantly encountered; and language has a feature of being vague. It is absolutely necessary to develop a model of the domain by having the software specialists work with the domain experts; however, that approach usually has some initial difficulties due to a fundamental communication barrier. The developers have their minds full of classes, methods, algorithms, patterns, and tend to always make a match between a real life concept and a programming artifact.
The domain model should form a common language for describing system requirements, that works equally well for the business users or sponsors and for the software developers.
A project faces serious problems when its language is fractured. Domain experts use their jargon while technical team members have their own language tuned for discussing the domain in terms of design.Services
In the ubiquitous language the Nouns are mapped in to objects, and Verbs that are associated with corresponding nouns become the behaviors of those objects. But there are some actions in the domain, some verbs, which do not seem to belong to any object. Adding such behavior to an object would spoil the object, making it stand for functionality which does not belong to it. Perhaps those behaviors can occur in several objects across different classes. Best practice is to declare such behaviors as Services
A service is an operation offered as an interface that stands alone in the model; it provides functionality for the domain. Services tend to be named for what they can do (verbs rather than nouns). A good service has three characteristics
- The operation relates to a domain concept that is not a natural part of an entity or value object
- The operation performed refers to other objects in the domain.
- The operation is stateless (does not maintain or update its own internal state in response to being invoked)
While using Services, is important to keep the domain layer isolated. Because of it is easy to get confused between services which belong to the domain layer, and those belonging to the infrastructure layer. There can also be services in the application layer Domain and application SERVICES collaborate with the infrastructure SERVICES. It can be harder to distinguish application SERVICES from domain SERVICES. Following is the example that describes ho the different services can be divided in to layers.
Partitioning Services into Layers
Application Funds Transfer App Service
– Digests input (such as an XML request).
– Sends message to domain service for fulfillment.
– Listens for confirmation.
– Decides to send notification using infrastructure service.
• Domain Funds Transfer Domain Service
– Interacts with necessary Account and Ledger objects, making appropriate debits and credits.
– Supplies confirmation of result (transfer allowed or not, and so on).
• Infrastructure Send Notification Service
– Sends e-mails, letters, and other communications as directed by the application.
In a complex system the model tends to grow bigger and bigger. 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. They provide two views on a model
· One view provides details within an individual module
· The second view provides information about relationships between modules
Using modules in design is a way to increase cohesion and decrease coupling. It is recommended to group highly related classes into modules to provide maximum cohesion possible. Widely used cohesions
Communicational cohesion: Parts of the module work with same data
Functional cohesion: Parts of the module work together to perform well-defined tasks
Models need to refine until it partitions according to high-level domain concepts and the corresponding code is decoupled as well. Modules and their names should reflect insight into the domain becoming part of the ubiquitous language. After the role of the module is decided, it usually stays unchanged, because module refactoring may be more expensive than a class refactoring
Domain objects go through a set of states during their life time. They are created, placed in memory and used in computations, and they are destroyed. In some cases they are saved in permanent locations, like a database, where they can be retrieved from some time later, or they can be archived. At some point they can be completely erased from the system, including database and the archive storage. Managing the life cycle of a domain object constitutes a challenge in itself, and if it is not done properly, it may have a negative impact on the domain model. Aggregates, Factories and Repositories are three patterns which help us deal with it. Aggregate is a domain pattern used to define object ownership and boundaries. Factories and Repositories are two design patterns which help us deal with object creation and storage.
It is difficult to guarantee the consistency of changes to objects in a model with complex associations such as one-many and many to many associations. Many times invariants apply to closely related objects, not just discrete ones. Yet cautious locking schemes cause multiple users to interfere pointlessly with each other and make a system unusable.
Therefore, we use Aggregates.
An Aggregate is a group of associated objects which are considered as one unit with regard to data changes. Each Aggregate has one root. The root is an Entity, and it is the only object accessible from outside. The root Entity has global identity, and is responsible for maintaining the invariants. If there are other Entities inside the boundary, the identity of those entities is local, making sense only inside the aggregate. Only Aggregate roots can be obtained directly with database queries. All other objects must be found by traversal of associations. Objects within the Aggregate can hold references to other Aggregate roots. A delete operation must remove everything within the Aggregate boundary at once. When a change to any object within the Aggregate boundary is committed, all invariants of the whole Aggregate must be satisfied.
Nothing outside the Aggregate boundary can hold a reference to anything inside, except to the root entity. The root entity can hand references to the internal entities to other objects, but those objects can use them only transiently, and they may not hold on to the reference. The root may hand a copy of a value object to another object, and it doesn't matter what happens to it, because it's just a value and no longer will have any association with the Aggregate.
How is the Aggregate ensuring data integrity and enforcing the invariants?
Since other objects can hold references only to the root, it means that they cannot directly change the other objects in the aggregate. All they can do is to change the root, or ask the root to perform some actions. And the root will be able to change the other objects, but that is an operation contained inside the aggregate, and it is controllable. If the root is deleted and removed from memory, all the other objects from the aggregate will be deleted too, because there is no other object holding reference to any of them. When any change is done to the root which indirectly affects the other objects in the aggregate, it is simple to enforce the invariants because the root will do that. It is much harder to do so when external objects have direct access to internal ones and change them. Enforcing the invariants in such a circumstance involves putting some logic in external objects to deal with it, which is not desirable.
As an example, the customer is the root of the Aggregate, and all the other objects are internal. If the Address is needed, a copy of it can be passed to external objects.
It is too complex to create in the constructor of the root entity as Entities and Aggregates can often be large and complex. Object creation involves a lot of knowledge about the internal structure of the object, about the relationships between the objects contained, and the rules applied to them. This means that each client of the object will hold specific knowledge about the object built. This breaks encapsulation of the domain objects and of the Aggregates. Therefore we use concept of Factories; Factories are used to encapsulate the knowledge necessary for object creation, and they are especially useful to create Aggregates. When the root of the Aggregate is created, all the objects contained by the Aggregate are created along with it, and all the invariants are enforced. It is important for the creation process to be atomic.
There are several design patterns used to implement Factories. Two patterns among others are: Factory Method, Abstract Factory. A Factory Method is an object method which contains and hides knowledge necessary to create another object. This is very useful when a client wants to create an object which belongs to an Aggregate. The solution is to add a method to the Aggregate root, which takes care of the object creation, enforces all invariants, and returns a reference to that object, or to a copy of it.
There are times when the construction of an object is more complex, or when the creation of an object involves the creation of a series of objects. For example: the creation of an Aggregate. Hiding the internal construction needs of an Aggregate can be done in a separate Factory object which is dedicated to this task.
When creating a Factory, we are forced to violate an object’s encapsulation, which must be done carefully. Whenever something changes in the object that has an impact on construction rules or on some of the invariants, we need to make sure the Factory is updated to support the new condition. Factories are tightly related to the objects they are created. That can be a weakness, but it can also be strength.
Entity Factories and Value Object Factories are different. Values are usually immutable objects, and all the necessary attributes need to be produced at the time of creation. When the object is created, it has to be valid and final. It won’t change. Entities are not immutable. They can be changed later, by setting some of the attributes with the mention that all invariants need to be respected. Another difference comes from the fact that Entities need identity, while Value Objects do not.
There are times when a Factory is not needed, and a simple constructor is enough. Use a constructor when:
- The construction is not complicated.
- The creation of an object does not involve the creation of others, and all the attributes needed are passed via the constructor.
- The client is interested in the implementation, perhaps wants to choose the Strategy used.
- The class is the type. There is no hierarchy involved, so no need to choose between a list of concrete implementations.
A constructor or a Factory takes care of object creation. The entire purpose of creating objects is to use them.
In an object-oriented language, one must hold a reference to an object in order to be able to use it. It becomes a problem because one must make sure the client always has a reference to the object needed, or to another which has a reference to the respective object. Such a rule will enforce the objects to hold on a series of references. This increases coupling, creating a series of associations which are not really needed.
Repository, the purpose of which is to encapsulate all the logic needed to obtain object references. The domain objects won’t have to deal with the infrastructure to get the needed references to other objects of the domain. They will just get them from the Repository
The Repository may store references to some of the objects. When an object is created, it may be saved in the Repository, and retrieved from there to be used later. If the client requested an object from the Repository, and the Repository does not have it, it may get it from the storage. Either way, the Repository acts as a storage place for globally accessible objects.
The Repository may also include a Strategy. It may access one persistence storage or another based on the specified Strategy. It may use different storage locations for different type of objects. The overall effect is that the domain model is decoupled from the need of storing objects or their references, and accessing the underlying persistence infrastructure.
There is a relationship between Factory and Repository. They are both patterns of the model-driven design, and they both help us to manage the life cycle of domain objects. While the Factory is concerned with the creation of objects, the Repository takes care of already existing objects. The Repository may cache objects locally, but most often it needs to retrieve them from a persistent storage. Objects are either created using a constructor or they are passed to a Factory to be constructed. For this reason, the Repository may be seen as a Factory, because it creates objects. It is not a creation from scratch, but a reconstitution of an object which existed. We should not mix a Repository with a Factory. The Factory should create new objects, while the Repository should find already created objects.
Refactoring is the process of redesigning the code to make it better without changing application behavior. Traditionally, refactoring is described in terms of code transformations with technical motivations. It’s required to look at the code from time to time during the design and development process. Because refactoring may be a requirement. It is usually done in small steps which are controllable. The result is also a series of small improvements to the code. With refactoring we should not break the functionality of the system or should not introduce some bugs. The purpose of refactoring is to make the code better.
Code refactoring can be done in many ways. Even there exist refactoring patterns which represent an automated approach to refactoring. Another type of refactoring which is related to the domain and its model bring some insights to the domain, it gives things clearer, or discover relationship between two elements. Technical refactoring is another type of refactoring. It is also based on patterns & it can be organized and structured.
A solid design resists refactoring, so that the design needs to be flexible. Otherwise whenever a change is needed, you will find it difficult to deal with.
Domain centric means to derive all design from a business architecture perspective.
Domain-driven design (DDD) is an approach to the design of software, based on two premises .For most software projects, the primary focus should be on the domain and domain logic (as opposed to being the particular technology used to implement the system) Complex domain designs should be based on a model.
The term was coined by Eric Evans' book of that title. The book articulates a number of high-level concepts and practices, such as: Ubiquitous language: the domain model should form a common language for describing system requirements, that works equally well for the business users or sponsors and for the software developers. plus a number of specific software design patterns, such as: Entities (a.k.a. Reference Objects): An object in the domain model that is not defined by its attributes, but rather by a thread of continuity and identity. Value Objects: An object that has no conceptual identity. These objects describe a characteristic of a thing. Repository: methods for retrieving domain objects should delegate to a specialized 'repository' object such that alternative implementations may be easily interchanged. Factory: methods for creating domain objects should delegate to a specialized 'factory' object such that alternative implementations may be easily interchanged. Service: When an operation does not conceptually belong to any object. Following the natural contours of the problem, you can implement these operations in services.
The goal is to construct a technology independent application that retains business objects in original form. To do this an application’s architecture is decomposed
This approach while simple supports long term growth using highly cohesive and loosely coupled software layers that allow change in technology and business processes that would otherwise cause failure in most software structures.
This is where you find the user interface logic. This logic captures how a user (or other actor) interacts with the application. This interaction includes a common look & feel, and work flow specific to each actor. In addition to each view there may be more than one type of presentation, i.e., (Web, Thick Client, Wireless Application Protocol, etc).
All presentations and views are built on top of the same domain model. Handles presentation layer requests, Defines jobs the software is supposed to do. Coordinates tasks and delegates work to collaboration of domain objects in the next layer down. Can have state that reflect the progress of a task for the user or the program.
This layer mediates between the various user interface components on a GUI screen and translates the messages that they understand into messages understood by the objects in the domain model. It is responsible for the flow of the application and controls navigation from window to window. This layer is often partially generated by a window-builder and partially coded by the developer.
Heart of business software. Technical detail of storing these rules is delegated to infrastructure. Handles application layer requests. A couple of key design goals are
Capture and retaining the business architecture in its purest form. Support for multiple presentations and views without need for specific customization of the domain layer to the needs of any one specific point of view.
When driving software construction from the domain layer the concepts within this layer are reflected outward throughout the other layers in a vertical fashion. Once again reinforcing the idea of driving design from the business architecture.
Persistence is where we map data to/from the domain model and long-term storage
By encapsulating persistence in its own layer of functionality it could even be implemented as a remote interface to a distributed system instead of directly talking to an underlying storage device, and from the domain model’s perspective this would be transparent.
Benefits of Layered Architecture
Separating the domain layer from the infrastructure and user interface layers allows a much clearer design of each layer. Isolated layers are much less expensive to maintain, because they tend to evolve at different rates and respond to different needs. This seperation leads to reuse.
1. Modularity, low-coupling – easier maintenance.
2. Business-logic is separated from presentation – reuse.
3. General technical services, e.g., database, are
separated from the business-logic – reused, replaced.
4. Low coupling, separation of concerns – evolving functionality, system scaling-up, adapt to new Technologies
The separation also helps with deployment in distributed system, by allowing different layers to be placed flexibly in different servers or clients, in order to minimize communication overhead and improve performance.
Added by Indika (044051)
Smart UI Anti Pattern
So far we have discussed layered architecture related to domain isolation. Actually it is ideal for software project with more functionalities and more complex domain requirement.
Consider a small project with simple functionalities. If we select domain driven approach the design of the project will became more sophisticated. It may require some time to design the architecture in layer wise But more frequently those projects are given limited time to complete. This type of situation to it is better to choose smart UI anti pattern approach to carryout the project in very simpler and productive way.
What is Smart UI Anti pattern?
Smart UI anti pattern is alternative for layered approach and mutually exclusive from domain isolation. It decouples all the layer functionalities and embedded in to the user interface. So we can’t see any domain isolation here.
Where to apply Smart UI anti pattern?
Suppose Software Company is given a project with simple functionality and it has short time period to finish the project. Still all the domain requirement has not been verified it will take some time to clarify the entire domain requirement. Also there is a possibility of changing business functionality is very high.
Suppose a company has junior staff and they are not experience in domain driven design approach at all. If the management decided to do the project using domain driven approach, it will require considerable time to train the staff. But it is not productive with given time schedule of the project. And also it has some risk as they are doing this type of project in first time. So the possibility of the failure is very high.
There may be situations all the domain requirement has not been clarified and but customer need some modules to be finished with clarified requirements. If we select domain driven approach we have to wait until the domain layer is finished. So it is not possible to release a module of the project with identified requirements. So again smart UI anti pattern approach is ideal to address these types of situations.
1. Productivity is high and immediate for simple applications.
It will take less time to finish each module and earn profits.
2. Less capable developers can work this way with little training
3. Even deficiencies in requirement analysis can be overcome by releasing a prototype to users and then quickly changing the product to fit there requests.
4. Applications are decoupled from each other, so that delivery schedule of small modules can be planned relatively accurately. Expanding the system with additional, simple behavior can be very easy.
5. Relational databases work well and provide integration at the data level.
6. When applications are hand off, maintenance programmers will be able to quickly redo portions they cant figure out, because the effect of the changes should be localize to each particular UI.
1. Integration of applications is difficult to except through the databases
2. There is no reuse of behavior and no abstraction of the business problem. Business rules have to be duplicated in each operation to which they apply.
3. Rapid prototyping and iteration reach a natural limit because the lack of abstraction limit refactoring options.