9
Common Problems for Teams Starting Out with Domain-Driven Design

WHAT’S IN THIS CHAPTER?

  • Understanding why Domain-Driven Design is about more than just writing code
  • Avoiding the trap of overemphasizing the tactical patterns
  • Why incorrectly applying Domain-Driven Design will make simple problems more complex and frustrate teams
  • Realizing that strategic design, collaboration, and communication are more important than the Domain-Driven Design pattern language
  • The wasted effort of teams not focusing on the core domain
  • The pitfalls teams fall into when applying Domain-Driven Design without a domain expert or an iterative development methodology
  • The antipattern of striving for Domain-Driven Design perfection

Domain-Driven Design (DDD) is a philosophy born out of a need to realign the focus of development teams writing software for complex domains. It is not a framework or a set of predefined templates that you can apply to a project. Although there is value in design patterns and frameworks, DDD emphasizes the value in collaboration, exploration, and learning between the business and development team to produce useful and relevant software. Teams should embrace the problem domain they are working within and look outside of their technical tunnel vision. The most fundamental point to being a great software craftsman is to understand the problem domain you work within and value this as much as you value your technical expertise.

Teams new to DDD or those that do not understand the core concepts can experience problems when applying the philosophy to their projects. These common problems are presented here with explanations as to why teams are finding it difficult to adopt the philosophy.

Overemphasizing the Importance of Tactical Patterns

DDD presents a selection of tactical patterns to help with Model-Driven Design and to aid in the creation of a domain model. A quick Google search on DDD shows you that these building blocks have been overemphasized and are often mistakenly thought of as the most important part of DDD. Evans himself often remarks that he wished he had put the more strategic patterns of DDD rather than the building block patterns at the beginning of the book because most people seem to stop reading after this set of patterns.

A focus on the tactical coding patterns of DDD highlights a bigger problem: technical people who are only focused on technical patterns and writing code. When designing software for systems with complex logic, typing code will never become a bottleneck. The code is an artifact of developers and domain experts working together and modeling the problem domain. The code represents the end of the process of collaboration and discovery. A developer’s job is to problem solve, and problem solving is sometimes easier done away from the keyboard in collaboration with domain experts. In the end, working code is ultimately the result of learning and understanding the domain.

Using the Same Architecture for All Bounded Contexts

DDD does not dictate a framework, tool set, or application architecture. You don’t have to use CQRS, event sourcing, event-driven, RESTful services, messaging, or object-relational mappers to apply the principles of DDD. What it does insist on, though, is that the complexity of your domain model is kept isolated from the complexities of your technical code. Any architecture that supports this is a good fit for DDD. Domain logic and technical complexity change at different rates; as a result, the organization of these different contexts is key to managing complexity.

Architectures are bounded context and not application specific. The architect for a simple bounded context with low complexity could be composed of a combination of a layered design with the transaction script pattern for the domain layer. A more collaborative domain could employ CQRS, and a complex domain would favor the rich domain model pattern.

Striving for Tactical Pattern Perfection

Teams concerned only with writing code focus on the tactical patterns of DDD. They treat the building block patterns as a bible rather than a guide, with no understanding of when it’s okay to break the rules. They spend wasted effort adhering to the rules of the patterns. This energy is better spent on understanding why it needs to be written in the first place. DDD is about discovering what you need to write, why you need to write it, and how much effort you should use. As mentioned before, the tactical patterns of DDD are the elements that have evolved the most since Eric’s book was written, with the strategic side of DDD remaining faithful to Eric Evan’s original text. How development teams create domain models is not nearly as important as understanding what models to write in the first place and how to develop them in a bounded context. Understanding the what and the why of problem solving is a more important process than how you are going to implement it in code.

Mistaking the Building Blocks for the Value of DDD

Many DDD projects fail because the tactical patterns of DDD are picked, but the strategic and collaborative sides of DDD are neglected. Teams do not take the time to knowledge-crunch with the business. They do not concentrate on the domain model and on careful abstractions. They don’t establish a ubiquitous language (UL). Using only the tactical pattern language of DDD is known as DDD lite. Following a DDD lite approach is fine, but this is not embracing the DDD philosophy. Teams mistakenly thinking that they are applying DDD will be missing out on much of where the value of DDD lies: collaboration, UL, and bounded contexts. Focusing only on the patterns to aid the modeling design omits the bigger picture of problem solving.

In contrast, many of the strategic patterns of DDD can be used in the creation of any medium-to-large-scale business system regardless of the underlying complexity. In fact, all the strategic patterns have many benefits, including identifying whether a UL should be defined and whether the tactical patterns should be used at all. Subdomains can help break down complex problem domains to aid communication and identify what is important. Context maps reveal integration points between different contexts along with the relationships between teams. However, it is sometimes difficult to justify the tactical patterns of applying the domain model pattern to anything other than a complex or constantly evolving domain.

Focusing on Code Rather Than the Principles of DDD

One of the most often-asked questions on software development forums is this: Can I see a sample application? There are probably many good solutions that show the result of a product developed under a DDD process, but much of the benefit of DDD is not revealed when you only examine the code artifact. DDD is performed on whiteboards, over coffee, and in the corridors with business experts; it manifests itself when a handful of small refactorings suddenly reveal a hidden domain concept that provides the key to deeper insight. A sample application does not reveal the many conversations and collaborations between domain experts and the development team.

The code artifact is the product of months and months of hard work, but it only represents the last iteration. The code itself would have been through a number of guises before it reached what it resembles today. Over time, the code will continue to evolve to support the changing business requirements; a model that is useful today may look vastly different to the model used in future iterations of the product.

If you were to view a solution that had been built following a DDD approach hoping to emulate the philosophy, a lot of the principles and practices would not be experienced, and too much emphasis would be placed on the building blocks of the code. Indeed, if you were not familiar with the domain, you would not find the underlying domain model very expressive.

DDD does prescribe a set of design best practices, patterns, and building blocks that are often mistakenly thought to be core to applying DDD to a product. Instead, think of these design artifacts as merely a means to an end used to represent the conceptual model. The heart of DDD lies deep in the collaboration between the development team and domain experts to produce a useful model.

Missing the Real Value of DDD: Collaboration, Communication, and Context

A team focusing too much on the tactical patterns is missing the point of DDD. The true value of DDD lies in the creation of a shared language, specific to a context that enables developers and domain experts to collaborate on solutions effectively. Code is a by-product of this collaboration. The removal of ambiguity in conversations and effortless communication is the goal. These foundations must be in place before any coding takes place to give teams the best chance of solving problems. When development does start to focus on language, context and collaboration enable code to be well organized and bound to the mental models of the business.

Problems are solved not only in code but through collaboration, communication, and exploration with domain experts. Developers should not be judged on how quickly they can churn out code; they must be judged on how they solve problems.

Producing a Big Ball of Mud Due to Underestimating the Importance of Context

Context, context, context, and more context. It’s a fundamental concept in DDD. Context helps you organize solutions for large problem domains. All problems cannot be solved using the same model. Various models need to be created to solve different problems. Creating models within defined context boundaries is essential to keep your code in a manageable state and avoid it turning into a Big Ball of Mud (BBoM). Understanding where contexts end and begin is the responsibility of a context map. Without the notion of context and a context map to guide you, teams cannot deliver value because they are constantly fighting the unorganized mess of their codebase.

If teams don’t understand other contexts, changes they make may bleed into those other contexts. Teams without a clear understanding of the boundaries of a model risk violating its conceptual integrity. Blurred or no lines of applicability between models often results in a merging of models, which quickly leads to a BBoM. A context map is vital to understanding boundary lines and how to uphold the integrity of models.

The biggest issue that contributes to legacy code and technical debt is how it’s organized. Code is easy to write, but without due care and attention to how it is structured, it can become extremely hard to read. Understanding about context enables you to isolate unrelated concepts so that models are more pure and focused. Think about it like applying the Single Responsibility Principle (SRP) but at an architectural level. Code that is easier to maintain and read will allow teams to deliver value faster, which is the essence of DDD.

Recognize when domain experts are talking in a different context but still using the same terms. If the same terms are used within the business, it is easy to fall into the trap of thinking that the models can be reused. Beware implicit models that are used for more than one context. It’s better to create explicit models. Apply the principle of Don’t Repeat Yourself (DRY) to a bounded context only and not a system. Don’t be afraid to use the same concepts and names in different contexts. The most important thing teams need to know about is that they should protect their boundaries.

Causing Ambiguity and Misinterpretations by Failing to Create a UL

Effective communication is essential for understanding and solving challenges within the problem domain. Without strong communication, collaboration between the development team and domain expert cannot flourish. Teams that do not value the need for a shared language are likely to employ technical abstractions and build a model using their own shared technical language. When the teams seek help or validation on their model, they are required to translate it for domain experts to understand. At best, this translation is a bottleneck for development; at worst, it can end up with the team building around concepts and themes that are not important or that the domain experts do not understand.

Without a shared language, you cannot create a shared model. This shared vision of the problem enables the capturing of implicit concepts and collaborative problem solving. The process of creating a language is a direct result of collaboration between the development team and domain experts. Being able to easily solve problems and understand the problem domain is where the payoff comes from.

Without a UL, contexts are hard to discover, because a bounded context is primarily defined by the applicability of language. Models created without context and explicit language quickly turn into a BBoM as concepts with the same name are modeled as one.

The formulation of a language can have a big impact on a business and product development. It helps to explicitly define common concepts, and just like a pattern language, remove ambiguity when talking through complex domain logic and business capabilities.

With a UL, domain experts can offer solutions to software problems when implementing the domain model as much as the development teams themselves.

Designing Technical-Focused Solutions Due to a Lack of Collaboration

Without collaboration, key concepts of the problem domain can be missed, and easy-to-understand concepts can be overemphasized. Within an organization, important facets of a domain are often implicit; teams not working with domain experts can overlook these, instead focusing on the lower-hanging fruit like the nouns of a domain. Without collaboration to validate understanding and reveal hinted-at concepts, development teams will abstract around technical terms, and business users will require translation to understand how the problems in the solution space relate to the problem space.

Collaboration is all about getting lots of people with different points of view working on creating a model of the problem domain that can be used to solve problems. No one has the authority on a good idea, and no suggestion is stupid.

Trying to collaborate on knowledge crunching with anyone other than a domain expert can be a wasted effort. A business analyst who may act as a proxy for a domain expert will be able to give you requirements and communicate inputs and outputs, but he will not be able to assist with shaping a model to fulfill the use cases.

Spending Too Much Time on What’s Not Important

Teams must understand the underlying reason why they are developing software instead of integrating an off-the-shelf solution. Understanding the strategic vision and the choice of build over buy helps teams concentrate effort. Without a focus on what is core to the success of a project, teams with a limited resource will not apply that resource in the most important areas — the core domains. Spreading resources too thin in the most important areas of a project is an antipattern.

The design of software will suffer without a clear and shared vision of the goal of the product, often captured using a domain vision statement. Well-informed and educated design decisions can be made when developers understand the intent behind business users’ requirements. Missing the intent of the business and blindly developing leads to poor results.

Accepting that not all of a system will be well designed and that not all of a system needs to be well designed is a big step forward for a team. Without a focus on the key aspects of a system, talented members of a team may be distracted by frameworks and instead want to work on the latest JavaScript framework at the presentation layer instead of the core aspects of a product.

In addition to teams, it’s important that domain experts have a clear understanding of the core domain. A domain expert who does not share the vision of a stakeholder or is unsure why the software is being written will not be effective during knowledge-crunching sessions due to negativity or confusion.

Making Simple Problems Complex

Applying techniques designed to manage complex problems to domains with little or no complexity will result in at best wasted effort and at worst needlessly complicated solutions that are difficult to maintain due to the multiple layers of abstractions. DDD is best used for strategically important applications; otherwise, the deep knowledge gained during DDD provides little value to the organization.

When creating a system, developers should strive for simplicity and clarity. Software that is full of abstractions achieves little more than satisfying developers’ egos and obscuring the reality of a simple codebase. Developers who aren’t engaged with delivering value and are instead only focused on technical endeavors will invent complexity because they’re bored by the business problem. This kind of software design can lead to frustration for teams in the future that need to maintain the mess of technical layers.

Applying DDD Principles to a Trivial Domain with Little Business Expectation

Simple problems require simple solutions. Trivial domains or subdomains that do not hold a strategic advantage for businesses will not benefit from all the principles of DDD. Developers who are keen to apply the principles of DDD to any project regardless of the complexity of the problem domain will likely be met with frustrated business colleagues who are not concerned with the less important areas of a business.

DDD is a set of principles to help you manage complex problem domains that hold significant advantage for a business. Problems with a low business expectation should be tackled in a no-thrills manner. This is not to say that they should be built in a haphazard manner. On the contrary, they should be built to be performant and maintainable, but problems that have little logic need straightforward solutions.

Large complex systems will have many subdomains, some containing complex logic key to strategic importance of a product, whereas others will simply be forms to manage data with little or no complexity. The tactical patterns of DDD along with collaborating to build a UL to communicate models should be reserved for the core subdomains only. These are the areas that need to be clear to aid rapid change or that model complicated and intrinsic logic. Teams should not waste energy on the generic domains or subdomains beyond keeping them working and isolated from the core domains.

Disregarding CRUD as an Antipattern

Not all of your system will be well designed; trying to perfect an entire codebase can be wasted effort. Your focus and energy should be on the core domain, for anything else good is good enough. For systems with little or no domain logic and with no more than forms over data opt for a simpler form of architecture such as a create, read, update, and delete (CRUD)-based system to decrease time spent and increase availability for the core domain.

Using the Domain Model Pattern for Every Bounded Context

The domain model pattern is useful for complex or frequently changing models. The effort required to employ the domain model pattern for models that are generic or lack domain logic will be far greater than any value that will be gained. Utilize model-driven design and the domain model pattern for the core domain, and use other domain logic patterns for simpler parts of your system.

Ask Yourself: Is It Worth This Extra Complexity?

When junior developers learn about design patterns, they try to apply them to every piece of code they write. This behavior is often seen when teams learn about DDD. They focus only on the tactical patterns of DDD, blindly applying these patterns to every project regardless of whether it is justified. This eagerness to apply a new philosophy without due consideration as to whether the process is a worthwhile endeavor for the software can lead to needless complexity where a simple solution would have sufficed.

Don’t develop nonstrategic software if off-the-shelf software will suffice. If the effort to automate a manual process is too great, just leave it manual. Remember: Solutions don’t always have to be technical.

Underestimating the Cost of Applying DDD

Applying the principles of DDD is hard and costly both in time and resource. It makes sense to only fully apply them to the most important areas of your system: your core domain. The principles hang on a business willing to work with you on solutions rather than have you work in isolation. DDD often is more valuable to the nontechnical parts of product design.

Trying to Succeed Without a Motivated and Focused Team

DDD is not for everyone. It is certainly not the right fit for every project. To gain the most benefit when following DDD, you need a complex core domain that will be invested in over time, an iterative development process, and access to domain experts. However, that is not all that is required. There are a number of other skills that you need to succeed at DDD. To be effective at DDD, you need the following:

  • Solid design principles required for refactoring
  • Sharp design sense
  • A focused, motivated, and experienced team

You need disciplined developers who are willing to work with domain experts and understand the business rather than worry how they can wedge the latest JavaScript framework into a project.

Remember: DDD isn’t a silver bullet. Just as switching from an upfront waterfall approach to a more agile/XP project methodology didn’t solve all your software development problems, opting to follow DDD won’t suddenly produce better software. The common denominator in any successful project is a team of clever people who are passionate about what they are doing and who care about it succeeding.

Attempting Collaboration When a Domain Expert Is Not Behind the Project

Business ownership and investment especially from domain experts is key to successful collaboration. If a development team is working alongside domain experts who are not invested in the project or do not understand the intent or vision, they will unlikely discover a useful model, create a UL, and work as an effective team. Development teams are great at designing systems to handle challenges in the problem domain. However when collaborating with a domain expert, they can go further and work together to remove the need for software solutions by removing issues at the source and redefining business processes.

Learning in a Noniterative Development Methodology

DDD is about brainstorming. It’s about collaborative learning. It’s about not stopping at your first idea but continuing to experiment so you discover something better or simply to validate your initial idea. All this takes time, and a methodology that doesn’t support this can’t support DDD.

A useful model will not be created on the first attempt; therefore, an iterative development methodology is required to hone a design. Models evolve. Teams that don’t appreciate that the model and language are only valid for a given time will quickly see their useful creation turn into a BBoM. A model needs love. It needs to be refined and refactored as more insight into the domain is gained and as new use cases challenge the model.

It’s also worth noting that, to experiment and evolve a model, you need to have the safety of unit tests. That is not to say that you must follow a test-driven process; instead, you must ensure your code can be valid after a series of refactors.

Applying DDD to Every Problem

For every solution, there must be an appropriate problem. DDD is a design philosophy suited to a particular problem space. It is a great tool to have in your toolbox. However, if your business is not complex or isn’t changing frequently then don’t automatically reach for the DDD hammer: remember there are other better suited tools in your development tool kit. Only focus your modeling efforts and DDD on the most complex bounded contexts that bring the most value to your customer.

Not all subdomains are complex. Some domains or contexts may not even need a fully fledged domain model and may just contain data with no business logic that simply requires the basic CRUD operations. For low-complexity contexts, favor the use of a CRUD/Active Record/Transaction Script-based application, and leave the tactical patterns of DDD for parts of your system that are important to your customer, that are complex, and that change frequently.

Ask yourself: Is this extra effort helping you deliver your solution, or is it slowing you down? Keep things simple but not simplistic. Don’t over engineer a solution or try to leverage unhelpful frameworks. Keeping things simple is an art form and takes practice and a pragmatic mind-set.

Sacrificing Pragmatism for Needless Purity

Don’t try to strive for perfection in areas that don’t need it. For generic or supporting subdomains, keep things simple, straightforward, and uncomplicated. Use CRUD and simple domain logic patterns. Get the code written so it works; then move on to the core domain. The core domain is the area where you can strive for perfection. Small balls of mud are sometimes better in bounded contexts that are unimportant; they get the code written quickly and get it out of the way. If you need to change it, you can overwrite it. For areas of your product that are unimportant, unlikely to change or be invested in over time, favor working code over perfect code. Good is often good enough. Don’t worry if you are doing it right or start to seek confirmation; this will be wasted effort and a distraction. Leave purity for the areas that count.

Wasted Effort by Seeking Validation

When you build a system following the principles of DDD, you do not receive a certificate in the post from Eric Evans congratulating you on your achievement. Blindly following any patterns language or methodology without considering your own unique context is foolhardy. Trying to adhere to a set of rules for no other reason than to compile with a methodology is an antipattern. The DDD philosophy is not about following a set of rules or applying coding patterns. It is a process of learning. The journey is far more important than the destination, and the journey is all about exploring your problem domain in collaboration with domain experts rather than how many design patterns you can employ in your solution.

Search forums and read DDD blog posts to discover how teams are collaborating with the business to aid learning and increase discoveries. Don’t try to create the perfect repository pattern, and don’t seek confirmation from your peers who aren’t involved directly in your project because without the full context, you must take any advice lightly.

Always Striving for Beautiful Code

Teams that obsess with applying design patterns and principles regardless of the actual need will likely create overly complex and confusing architectures that miss the goal of the product in the first place. Teams should understand the motivations behind design patterns and use them judiciously. Blindly employing the tactical patterns of DDD does nothing to add value for the business.

A supple design in important areas that frequently change aids a model’s ability to be flexible and evolve without having large rippling effects. Painstakingly striving for elegant design in areas that offer little business value and will not be invested in is a waste of efforts. It is far better to have small balls of mud, isolated from other contexts that can easily be replaced, rather than trying to strive for beautiful code everywhere.

When working in the core domain, teams should certainly wait before committing to a pattern and a way of thinking. Delaying refactoring and living with the code to see what causes friction/changes the most can reveal more about the domain and lead to a more informed design choice.

Don’t be distracted by patterns, frameworks, or methodologies; they are implementation details. Your goal is to understand your domain at a deeper level to be best equipped to solve problems within it. This is the true value of DDD.

DDD Is About Providing Value

Don’t let design patterns and principles get in the way of getting things done and providing value to the business. Patterns and principles are guides for you to produce supple designs. Badges of honor will not be given out the more you use them in an application. DDD is about providing value, not producing elegant code.

The Salient Points

  • The tactical patterns of DDD can guide you toward creating an effective domain model; however, this area of DDD is evolving, and the implementation details have been overemphasized. The patterns may have value, but this is not where the value of DDD lies.
  • DDD is far more than coding. Collaboration with domain experts to knowledge crunch and have a shared understanding of the problem domain expressed in a ubiquitous language are the pillars of DDD.
  • Context is everything; context and isolation retain the integrity of your code. It reduces cognitive load and makes a model specific.
  • You need a smart dedicated team willing to learn about the domain.
  • You need access to a domain expert. Teams can’t reveal deeper insights without them.
  • Use CRUD for bounded contexts with low complexity. You are not a bad programmer if you don’t have a domain model.
  • Bounded context and the ubiquitous language are the foundation of DDD.
  • DDD is about the process of learning, refining, experimenting, and exploring in the quest to discover a useful model in collaboration.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.194.230