Making implicit concepts explicit
Making implicit concepts explicit
• Implicit concepts are concepts that exist but , for some reason, the
domain experts don’t talk about them explicitly. It takes some
time until the developers actually realize the existence of those
implicit concepts, by means of hints while discussing the model or
at later stages, when several other concepts are already set in
place.
Digging out concepts
• When a new concept is found, we need to refactor in order to reflect
that concept in the code, naturally ending up with a cleaner design.
Listen to the language
• Listen to the language the domain experts use.
• The language of the domain model has become more powerful.
• refactor the code to reflect the new model and find a cleaner design.
Scrutinize awkwardness
• Developers can not always find implicit concepts just by talking to
the domain experts.
• We need to dig concepts from the code itself. Whenever we find
code which is confusing, difficult to extend, does not fit into any
explicit concept, and has awkward responsibilities, we need to
actively engage the domain experts in order to clarify our domain
knowledge and further improve the domain design.
Contemplate contradictions:
• Different domain experts can have different views on a particular part of the
domain, according to their knowledge and experience.
• Most of the times, the contradictions are merely terminology differences and
misunderstandings, however sometimes there is an actual clash of ideas.
• Either way, when we find a contradiction, we need to clarify it with the
domain experts.
Read the book
• When dealing with a specific domain, for example “accounting”, it
is often useful to educate ourselves on it, specially if no domain
experts are available.
Try, try again
• All the knowledge crunching is interactive, it doesn’t happen in one
go. It involves a lot of experimentation, a lot of trial and error. It
takes time, but it deepens the model, it makes the model reflect what
is indeed needed and, in the long run, it ends up being faster.
Supple Design
• Supple design is the complement to deep modeling. Once you've dug out implicit
concepts and made them explicit, you have the raw material. Through the
iterative cycle, you hammer that material into a useful shape, cultivating a model
that simply and clearly captures the key concerns, and shaping a design that
allows a client developer to really put that model to work. Development of the
design and code leads to insight that refines model concepts.
Supple Design
Those techniques are
• Intention-Revealing Interfaces
• Side-Effect Free Functions
• Assertions
• Standalone Classes
• Closure of Operations
•
• Intention-Revealing Interfaces
• The interface of a class should reveal how that class is to be used If developers don’t
understand the interface (and have access to source), they will look at the
implementation to understand the class. At that point, the value of encapsulation is
lost
• So, name classes and methods to describe their effect and purpose, without reference
to their implementation
• These names should be drawn from the Ubiquitous Language
• Write a test case before the methods are implemented to force your thinking into how
Side-Effect-Free Functions
• Operations (methods) can be broadly divided into two categories commands
and queries
• Commands are operations that affect the state of the system. Side effects are
changes to the state of the system that are not obvious from the name of the
operation. They can occur when a command calls other commands which call
other commands etc.; the developer invoked one command, but ends up
changing multiple aspects of the system.
Assertions
After you have performed work on creating as many side-effect free functions and value
objects as possible, you are still going to have command operations on Entity objects.
Assertions typically state three things
the pre-conditions that must be true before an operation
the post-conditions that will be true after the operation
invariants that must always be true of a particular object
Standalone Classes
• Interdependencies make models and designs hard to understand
• They also make them hard to test
• We should do as much as possible to minimize dependencies in our models
• Modules and Aggregates are two techniques already discussed for doing
this; They don’t eliminate dependencies but tend to reduce and/ or limit
them in some way.
Closure of Operations
• certain operations are “closed” with respect to a particular type of
number;
• When it fits, define operations such that their return type is the same
as the type of its arguments; Such operations provides a high-level
interface without introducing any dependency on other concepts
Maintaining model integrity
When working in a large system, with a large model where we have several teams working on
it, a section of the model will likely end up being interpreted, and adjusted in code, in
different ways by the different teams.
These different interpretations and code adjustments will end up corrupting the model,
leading to incoherent code and eventually bugs.
The best way to deal with this issue is to break the large model into smaller independent
models. Breaking down a model is done by identifying bounded contexts and using context
maps to identify their borders and relationships between them.
Bounded context
It’s common, and natural, for projects to have several different models within them.
However, the boundaries in which the different models connect are fragile and prone to
lead to mistakes, unreliability and confusion. To prevent these issues, we should:
Explicitly define the context within which a model applies. Explicitly set boundaries in
terms of team organization, usage within specific parts of the application, and physical
manifestations such as code bases and database schemas. Keep the model strictly
consistent within these bounds, but don’t be distracted or confused by issues outside.
Bounded context
In different contexts, different models apply, with differences in terminology,
concepts and rules. The different models, used in different contexts can and
will overlap. They might relate to the same data, but represent it in a
different way. I am thinking for example of having a User entity in a
UserManagement context contain the whole User data, but in an Account
context the User entity might be just a fraction of the whole User data.
Bounded context
Recognizing splinters within a bounded context
Two types of problems arise when a bounded context starts to splinter:
Duplicate concepts: The same concept is represented and implemented in more than one way.
This means that when a concept changes, it has to be updated in several places, and the
developers need to be aware of the several different ways of doing one same thing, as well as
the subtle difference within them.
False cognates: When two people are using the same term and they think they are talking
about the same thing, but they are actually thinking of different things.
Bounded context
Context map:
When the bounded context boundaries are not explicitly clear, those boundaries tend to get blurred by
promiscuously mixing code that exists in different bounded contexts. This leads to code that is tightly
coupled and difficult to change in isolation.To make the bounded contexts ecosystems explicitly clear,
encapsulated, loosely coupled and high cohesive, we should:
Create a global view of all bounded contexts and their relations, using context maps, naming them and
adding them to the ubiquitous language. Identify the points of contact between bounded contexts,
together with the used translations and abstractions. All developers must know the boundaries and to
what context any given code unit belongs to.
relationship between bounded contexts
Context mapping in Domain Driven Design:
Context mapping is a tool that allows you to identify the
relationship between bounded contexts and the relationship
between the teams that are responsible for them.
relationship between bounded contexts
relationship between bounded contexts
several ways of how you can integrate between multiple bounded contexts:
Partnership
Shared kernel
Customer supplier
Conformist
Anticorruption Layer
Open Host Service
relationship between bounded contexts
Partnership
It describes more about the relationship between teams as oppose to actual code. This typically
happens when 2 teams working on 2 bounded contexts have aligned and dependent set of goals.
Each team should at least understand some of their partner’s Ubiquitous Language, namely the
things that are interesting to their own context. This can work fairly well when both bounded
contexts are at early stages of the project, where communication is quick and more efficient than
implementing some of the other techniques. When both sides become more established, teams
may incur too much commitment in understanding each other’s Ubiquitous Language. Of course,
if a team was to work on both such bounded contexts, the ‘Partnership’ relationship would be
relationship between bounded contexts
Shared Kernel
2 or more bounded contexts can share a common model. In design
terms, the Ubiquitous Language of this shared part is common both
all relevent teams. In code terms, you may have a shared library, or
a service. This is generally a small codebase but difficult to maintain
as its related bounded contexts develop, as the teams will tend to go
relationship between bounded contexts
Customer Supplier
This approach puts 2 bounded contexts into an upstream and
downstream, where the upstream is the supplier, and has to try and
meet the expectations of the customer (downstream). But the final
decision of what the customer gets comes from the supplier. This
typically works in an autonomous environment within the same
relationship between bounded contexts
Conformist
This relationship describes the relationship of 2 bounded context,
where the upstream has no interest in supporting the downstream for
whatever reason. Instead, the downstream must conform to whatever
the upstream provides. This can happen when integrating a new
feature with a large, existing solution that is well established; or using
relationship between bounded contexts
Anticorruption Layer (ACL)
This is another upstream/downstream relationship at a lower level, where the
downstream bounded context implements a layer between itself and the upstream. This
layer is responsible for translating the objects given by the upstream into its own
models. This approach will guarantee the integrity of the downstream bounded context
and keeps it completely ignorant of any foreign concepts. This approach is generally
useful for integrating new features to some existing legacy software, where you can treat
the existing legacy software as a black box bounded context and create an ACL for the
relationship between bounded contexts
Open Host Service (OHS) / Published Language (PL)
I will talk about these two approaches together as they both define a relationship where
the upstream provides a set of well documented or readily available information about
the integration models. This is built on top of the Conformist approach from earlier,
where the downstream is a lot more tolerable. The upstream will also need to provide
version support. Typically the upstream bounded context will support multiple clients
and have no interest in especially supporting particular one. For example, to conform to
Amazon APIs, the downstream will have confidence in the integration by understanding
Strategic Domain Driven Design with Context
Mapping
Many approaches to object oriented modeling tend not to scale well when the
applications grow in size and complexity. Context Mapping is a general purpose
technique, part of the Domain Driven Design (DDD) toolkit, helps the architects and
developers manage the many types of complexity they face in software development
projects. Different from other well known DDD patterns, context mapping applies
to any kind of software development scenario and provides a high level view that
might help the developers to take strategic decisions, like whether to go for a full
scale DDD implementation or not in their specific project environment.
Strategic Domain Driven Design with Context
Mapping
Many approaches to object oriented modeling tend not to scale well when the
applications grow in size and complexity. Context Mapping is a general purpose
technique, part of the Domain Driven Design (DDD) toolkit, helps the architects and
developers manage the many types of complexity they face in software development
projects. Different from other well known DDD patterns, context mapping applies
to any kind of software development scenario and provides a high level view that
might help the developers to take strategic decisions, like whether to go for a full
scale DDD implementation or not in their specific project environment.
Context mapping in Domain Driven Design:
Context mapping is a tool that allows you to identify the
relationship between bounded contexts and the relationship
between the teams that are responsible for them.
relationship between bounded contexts
several ways of how you can integrate between multiple bounded contexts:
Partnership
Shared kernel
Customer supplier
Conformist
Anticorruption Layer
Open Host Service
Domain
The domain is the real-world context which you are attempting to solve by writing a piece of software. Simply said, the
domain is the thing why software exists and is about. Evans divides the domain into three subdomains.
Core Domain: The core domain represents a competitive advantage and the software’s reason for being. It is a hearth of the
software with the most fundamental business rules, use cases, and domain models. It usually is the smallest part of the
codebase. In other words, it is the most critical 20% part of the software.
Supporting Domain: Supporting domains are supporting subsystems of software. The core can still exist without them but
does not makes it alone to its users. There can be multiple supporting domains in one system. For example, the supporting
domain is for the core domain, the same thing as invoicing for Amazon.
Generic Domain: A generic domain is the least custom part and the least important part of the software. It is not specific to
your domain, and it is widespread in other systems. I am currently working on an employee’s timesheets information system,
and the generic domain for this software is an external time tracker for each of the employees.
Core Domain
Core Domain: The core domain represents a competitive advantage and the software’s reason
for being. It is a hearth of the software with the most fundamental business rules, use cases,
and domain models. It usually is the smallest part of the codebase. In other words, it is the
most critical 20% part of the software.
Generic Domain: A generic domain is the least custom part and the least important part of the
software. It is not specific to your domain, and it is widespread in other systems. I am
currently working on an employee’s timesheets information system, and the generic domain
for this software is an external time tracker for each of the employees.