<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/software/</id>
  <link href="https://www.endpointdev.com/blog/tags/software/"/>
  <link href="https://www.endpointdev.com/blog/tags/software/" rel="self"/>
  <updated>2026-05-26T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Applying Domain-Driven Design in Practice</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2026/05/applying-it-in-practice-ddd-part-4/"/>
      <id>https://www.endpointdev.com/blog/2026/05/applying-it-in-practice-ddd-part-4/</id>
      <published>2026-05-26T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2026/05/applying-it-in-practice-ddd-part-4/cover.webp&#34; alt=&#34;Weathered wooden logs crossed and stacked on a sandy desert landscape under an overcast sky.&#34;&gt;&lt;br&gt;
Photo by Josh Ausborne, 2007.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is part 4 of a series of blog posts on Domain-Driven Design:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/&#34;&gt;High level system analysis and design with Domain-Driven Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/04/implementing-business-logic-ddd-part-2/&#34;&gt;Implementing business logic with Domain-Driven Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/&#34;&gt;Designing software architecture with Domain-Driven Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/05/applying-it-in-practice-ddd-part-4/&#34;&gt;Applying Domain-Driven Design in Practice&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Domain-Driven Design&lt;/strong&gt; is an approach to software development that focuses on, &lt;a href=&#34;https://www.oreilly.com/library/view/domain-driven-design-tackling/0321125215/&#34;&gt;as Eric Evans puts it&lt;/a&gt;, &amp;ldquo;tackling the complexity in the heart of software&amp;rdquo;. And what is in the heart of software? The business domain in which it operates. Or more specifically: a &lt;strong&gt;model&lt;/strong&gt; of it, made of code. That is, the code that implements the business logic that comes into play when solving problems within the realm of a particular business activity.&lt;/p&gt;
&lt;p&gt;DDD is not just about writing code though. It&amp;rsquo;s a whole methodology that touches on business needs, requirements gathering, organizational dynamics, high level architectural design, and lower level patterns for implementing software intensive systems.&lt;/p&gt;
&lt;p&gt;As a result, DDD offers a treasure trove of concepts, patterns and tools that can be applied to any software project, regardless of the size and complexity.&lt;/p&gt;
&lt;p&gt;In this series of blog posts we&amp;rsquo;re going to explore many aspects of DDD. We will be following the structure laid out by &lt;a href=&#34;https://vladikk.com/&#34;&gt;Vlad Khononov&lt;/a&gt;&amp;rsquo;s excellent book on the topic &amp;ldquo;&lt;a href=&#34;https://www.oreilly.com/library/view/learning-domain-driven-design/9781098100124/&#34;&gt;Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy&lt;/a&gt;&amp;rdquo;. So you can think of this series as a summary of that book. An abridged version that can serve as a review for anybody who has read it; but also as an entry point for people who are new to DDD.&lt;/p&gt;
&lt;h3 id=&#34;table-of-contents&#34;&gt;Table of contents&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#applying-domain-driven-design-in-practice&#34;&gt;Applying Domain-Driven Design in Practice&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#table-of-contents&#34;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-10-design-heuristics&#34;&gt;Section 10: Design heuristics&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#bounded-contexts&#34;&gt;Bounded contexts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#business-logic-implementation-patterns&#34;&gt;Business logic implementation patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#architectural-patterns&#34;&gt;Architectural patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#testing-strategy&#34;&gt;Testing strategy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-11-evolving-design-decisions&#34;&gt;Section 11: Evolving design decisions&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#changes-in-the-domain&#34;&gt;Changes in the domain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#organizational-changes&#34;&gt;Organizational changes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#domain-knowledge-changes&#34;&gt;Domain knowledge changes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#growth&#34;&gt;Growth&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-12-domain-driven-design-in-the-real-world&#34;&gt;Section 12: Domain-driven design in the real world&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#strategic-analysis&#34;&gt;Strategic analysis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#modernization-strategy&#34;&gt;Modernization strategy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#selling-domain-driven-design&#34;&gt;Selling Domain-driven design&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#finally&#34;&gt;Finally&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;section-10-design-heuristics&#34;&gt;Section 10: Design heuristics&lt;/h3&gt;
&lt;p&gt;At this point in the series we&amp;rsquo;ve explored a number of tools and patterns to work with DDD. We also touched on what kinds of problems they solve and how to apply them. We learned how to analyze business domains, how to design high level components, how to implement business logic, how to organize code bases and how system components interact. In this section, we&amp;rsquo;ll discuss a series of heuristics that we can use to make decisions as to when to apply these techniques.&lt;/p&gt;
&lt;h4 id=&#34;bounded-contexts&#34;&gt;Bounded contexts&lt;/h4&gt;
&lt;p&gt;When designing &lt;strong&gt;bounded contexts&lt;/strong&gt;, that is, the physically separated system components, size is not a useful metric. Instead, consider the &lt;strong&gt;model&lt;/strong&gt; that they contain, and make sure they are cohesive. In fact, breaking down a system into many bounded contexts too early is a form of premature optimization that can be costly when done wrongly. And at the beginning of a project, when domain knowledge is likely as low as it will ever be, it&amp;rsquo;s easy to get it wrong.&lt;/p&gt;
&lt;p&gt;A system that&amp;rsquo;s broken up into many physical components is more expensive to maintain because changes usually propagate across them. This is due to obvious technical reasons, but can also be exacerbated by organizational reasons. For example, when the bounded contexts are maintained by different teams.&lt;/p&gt;
&lt;p&gt;So, &lt;strong&gt;start with bounded contexts with broader boundaries&lt;/strong&gt;. And then, as the system matures and the team acquires more domain knowledge, &lt;strong&gt;refactor into separate system components when needed&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&#34;business-logic-implementation-patterns&#34;&gt;Business logic implementation patterns&lt;/h4&gt;
&lt;p&gt;At its core, DDD is about &lt;strong&gt;letting the business domain drive software design decisions&lt;/strong&gt;. This means that we should strive to use the right tool for the job. As we&amp;rsquo;ve discussed before, the level of complexity of the domain at hand is what determines which &lt;strong&gt;business logic implementation patterns&lt;/strong&gt; should be applied. The more complex the problem at hand, the more elaborate the chosen pattern for the solution. Luckily, we learned that we can classify subdomains by their level of complexity, so we can use subdomain types to drive our decision making. This is explained in this diagram:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/applying-it-in-practice-ddd-part-4/business-logic-pattern-decision-tree.png&#34; alt=&#34;Business logic pattern decision tree&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Complexity, subdomain types and particular business requirements are what drive how we implement business logic.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;architectural-patterns&#34;&gt;Architectural patterns&lt;/h4&gt;
&lt;p&gt;Once we have decided how to implement the business logic, it&amp;rsquo;s easy to decide which &lt;strong&gt;architectural pattern&lt;/strong&gt; to develop our system with. The decision on what architecture to use is dependent on the business requirements and the business logic pattern we&amp;rsquo;ve selected:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/applying-it-in-practice-ddd-part-4/architectural-pattern-decision-tree.png&#34; alt=&#34;Architectural pattern decision tree&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The architecture is driven by whether we need multiple models to represent the same data and the business logic pattern.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If a system is using an event sourced domain model, or it needs multiple persistence models, CQRS is almost a requirement. Otherwise the system would be very limited in its querying capabilities. For a plain domain model, a ports and adapters architecture is ideal because it allows the domain model to be completely decoupled from data persistence concerns. For the active record pattern, it&amp;rsquo;s best to go with a layered architecture that includes a service layer. The service layer orchestrates the active records to execute the business operations. Finally, a simple three-layered architecture is sufficient for transaction scripts, where no advanced abstractions are needed to accommodate complex business logic.&lt;/p&gt;
&lt;h4 id=&#34;testing-strategy&#34;&gt;Testing strategy&lt;/h4&gt;
&lt;p&gt;Generally, there are three kinds of automated tests: &lt;strong&gt;end-to-end&lt;/strong&gt;, &lt;strong&gt;integration&lt;/strong&gt; and &lt;strong&gt;unit tests&lt;/strong&gt;. A test suite in any given system can choose to lean more into one kind and less on the others, forming one of three possible scenarios: the testing &lt;strong&gt;pyramid&lt;/strong&gt;, &lt;strong&gt;diamond&lt;/strong&gt; and &lt;strong&gt;reverse pyramid&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/applying-it-in-practice-ddd-part-4/testing-strategies.png&#34; alt=&#34;Testing strategies&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A test suite can be imbalanced in terms of the kinds of tests that it implements. Depending on the kind of system that is being tested, it is often more valuable to have more tests of one kind and fewer of others.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;How does it choose? It does so based on the patterns that it uses for implementing its business logic and its architecture. Here&amp;rsquo;s the decision tree:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/applying-it-in-practice-ddd-part-4/testing-strategy-decision-tree.png&#34; alt=&#34;Testing strategy decision tree&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The patterns we chose for business logic and architecture often give us a good idea of what kinds of tests we should focus more on.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A domain model leans heavily on components that are testable as units. Plain old objects like aggregates and value objects can be very well covered in isolation by unit tests. Active records on the other hand, are tightly coupled with database interaction logic. In these cases, business logic is usually distributed between the active records themselves, the database, and service objects. Integration tests are the most appropriate kinds of tests for this type of system, as they exercise multiple layers at once, and how they interact. Finally, pure transaction script systems benefit more from end-to-end tests. This is because their abstractions are shallow, so the system components are often hard to decompose and test in a meaningful way. Also, their logic is simple, so tests that exercise entire workflows can be very effective.&lt;/p&gt;
&lt;h3 id=&#34;section-11-evolving-design-decisions&#34;&gt;Section 11: Evolving design decisions&lt;/h3&gt;
&lt;p&gt;Of course, software very rarely stays static. On the contrary, it evolves. So far we&amp;rsquo;ve been mainly discussing how to create software that&amp;rsquo;s well designed using DDD&amp;rsquo;s concepts. However, if change is not managed correctly, not even the best designed software can resist becoming an unmaintainable mess that is prohibitively expensive to evolve. In this section we will discuss the main forces that produce change, and how to cope with it.&lt;/p&gt;
&lt;h4 id=&#34;changes-in-the-domain&#34;&gt;Changes in the domain&lt;/h4&gt;
&lt;p&gt;As we&amp;rsquo;ve seen before, &lt;strong&gt;it is crucial to know the business domain in which the software that we build operates&lt;/strong&gt;. In particular it&amp;rsquo;s important to decompose the domain into many subdomains and identify their type: core, supporting and generic. That knowledge drives important design decisions. As an organization evolves and grows though, there&amp;rsquo;s a good chance that the subdomains that compose it change from one type to another. Our designs need to react accordingly to these changes.&lt;/p&gt;
&lt;p&gt;Core and supporting subdomains can become &lt;strong&gt;commoditized&lt;/strong&gt; and become generic. For example, when off-the-shelf solutions become available that cover their functionality. Through emerging &lt;strong&gt;business opportunities&lt;/strong&gt;, generic and supporting subdomains can become core subdomains. This is the case when the company discovers a competitive advantage in an aspect of its business that it thought was nothing special. Likewise, a core subdomain can become a supporting one when it &lt;strong&gt;stops representing a business advantage&lt;/strong&gt; but still remains very unique to the business, prompting a divestment and simplification of its logic. Finally, generic subdomains being served by off-the-shelf solutions may be &lt;strong&gt;costly to acquire or integrate&lt;/strong&gt;. In such cases, a company might decide to go with an in-house solution, turning it into a supporting subdomain.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/applying-it-in-practice-ddd-part-4/subdomain-type-changes.png&#34; alt=&#34;Subdomain type changes&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Subdomain types are not static. These often change during the lifetime of a business.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Changes to subdomains directly affect the bounded contexts (i.e. the system&amp;rsquo;s high level physical components, applications, services, etc) that work with them, and their related high level design decisions. Namely, their integration patterns and their ownership.&lt;/p&gt;
&lt;p&gt;If a subdomain is becoming a core subdomain, &lt;strong&gt;we need to consider how its bounded contexts are integrated with others&lt;/strong&gt;. A core subdomain calls for an anticorruption layer in order to protect its model and for an open-host service with a published language in order to protect the models of its consumers. So, these patterns need to be implemented for the bounded contexts in the newly born core subdomain. Also, separate ways is a big no-no for core subdomains. We don&amp;rsquo;t want important and complex business logic to be duplicated and risk inconsistencies. So we have to make sure to move away from that, and implement one of the consumer-supplier patterns discussed earlier.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;ownership strategy is also determined by the type of subdomain&lt;/strong&gt;. When a subdomain morphs into a core subdomain, development needs to be moved in-house, or to trusted development partners. This is where domain experts are closest and the highest skill level is available. Supporting subdomain implementation and generic subdomain integration on the other hand, are fine to be outsourced or handled by more junior teams.&lt;/p&gt;
&lt;p&gt;Of course, &lt;strong&gt;a change in subdomain should also affect the way in which the business logic is modeled&lt;/strong&gt;. If the change is in the direction of more complexity, for example when a supporting subdomain becomes a core subdomain, that will manifest in the code base as an increase in difficulty when implementing new features, duplication and bugs; as the original design can no longer support the complexity that the business demands.&lt;/p&gt;
&lt;p&gt;In these cases, we need to refactor the business logic and use the pattern that best supports the new subdomain complexity needs. Here are a few pointers on how to transition from simpler business logic implementation patterns to more complex ones:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Transaction script to active record&lt;/strong&gt;. These are very similar in that they both model the business as procedural scripts. The advantage of active record is that it provides an abstraction over complex data structures. So, when refactoring transaction scripts into active records, look for direct data access and manipulation with SQL and move that logic into the active record objects. Start with the most complex structures.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Active record to domain model&lt;/strong&gt;. When the logic that orchestrates the active records becomes overly complex, duplicated and/or inconsistent, refactor into a domain model. Identify data structures that could be made immutable and encapsulate them and their related logic as value objects. Look for all logic that modifies system state and encapsulate it as methods inside the active records, then make all setters private. This makes sure all modifications of state happen inside the objects that are getting modified. These are your first rudimentary entities and commands. Next, examine the object hierarchies that are emerging, as these are your aggregates. Use the guiding rules of keeping them small, their transactional boundaries tight, and ensuring they contain only the data they need to be strongly consistent. Finally, identify a root for each aggregate. This will be the entry point for their public interface.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Domain model to event sourced domain model&lt;/strong&gt;. In order to transition into an event sourced domain model, the key is to stop modifying the aggregates&amp;rsquo; data directly and instead, produce domain events that reflect the changes. Make sure to capture those events reliably, and implement the projections necessary to take collections of events and turn them into fully hydrated aggregates. One tricky part of the migration is how to deal with the existing state-based data. There are two ways of doing this: One way is to model a &amp;ldquo;migration&amp;rdquo; event, which represents each aggregate in full, as they were before event sourcing was implemented. Another way is to try and reverse engineer a series of past events out of each aggregate in an attempt to approximate their history as if event sourcing was always present.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;organizational-changes&#34;&gt;Organizational changes&lt;/h4&gt;
&lt;p&gt;Organizational changes should also trigger changes in the system&amp;rsquo;s design. Namely, &lt;strong&gt;the strategies that bounded contexts use to communicate with each other&lt;/strong&gt;. These can change as a result of changes in the communication and collaboration levels of the teams involved.&lt;/p&gt;
&lt;p&gt;For example, when the development division grows to the point that new teams are created, a single bounded context that was maintained by the original smaller team needs to be separated into finer granularity ones. This way we maintain single team ownership for bounded contexts.&lt;/p&gt;
&lt;p&gt;Additionally, when teams that own bounded contexts that are tightly integrated grow distant, the integration patterns used need to evolve to reflect the new reality of the organization. A partnership style integration should evolve into a customer-supplier or even separate ways one.&lt;/p&gt;
&lt;h4 id=&#34;domain-knowledge-changes&#34;&gt;Domain knowledge changes&lt;/h4&gt;
&lt;p&gt;At its core, DDD is about having business domain knowledge drive software design decisions. As a greater understanding of the problem domain develops, so too should the design evolve to incorporate the new knowledge.&lt;/p&gt;
&lt;p&gt;The beginning of a project is often the time when we have the weakest understanding of the domain. We can use this realization to drive the decisions related to how we will decompose a system into bounded contexts. &lt;strong&gt;At the early stages, it&amp;rsquo;s better to err on the side of fewer separations&lt;/strong&gt;, and bigger bounded contexts with wider boundaries. This is because a premature separation that ends up being wrong is very costly to live with and very costly to fix later. The contrary is much less expensive and less risky: decomposing a monolithic system later when domain knowledge has solidified and we&amp;rsquo;re better equipped to make those decisions.&lt;/p&gt;
&lt;p&gt;Unfortunately, it is also possible for domain knowledge to get lost over time. Documentation can become stale and business experts and developers may leave the organization and leave a vacuum of knowledge. &lt;strong&gt;It is important to be proactive in maintaining the domain knowledge&lt;/strong&gt; via training, maintenance of documents, and ongoing discussions. The &lt;a href=&#34;https://en.wikipedia.org/wiki/Event_storming&#34;&gt;EventStorming workshop&lt;/a&gt; is a good tool for recovering lost knowledge.&lt;/p&gt;
&lt;h4 id=&#34;growth&#34;&gt;Growth&lt;/h4&gt;
&lt;p&gt;In software projects, growth is always desirable. After all, growth means that the software is useful, that the project is successful, and that stakeholders and users want to continue investing in it and evolving it with new capabilities. &lt;strong&gt;Unregulated growth however, can turn a code base into a big ball of mud&lt;/strong&gt;. This happens when changes and additions are done haphazardly without evaluating the original design decisions.&lt;/p&gt;
&lt;p&gt;To make sure a code base grows in a healthy manner, the key principle is this: &lt;strong&gt;identifying and eliminating accidental complexity&lt;/strong&gt;. That is, all complexity that is not essential to the problem domain. Complexity that arises by accident, due to bad design decisions. Here are a few aspects to keep in check to prevent build up of accidental complexity:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Subdomains&lt;/strong&gt;: As the business grows, the subdomains also morph, their types change and their boundaries shift. It&amp;rsquo;s important to keep track of how the subdomains are evolving because that gives us the ability to choose the appropriate technical solutions for them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bounded contexts&lt;/strong&gt;: Growth can also cause the boundaries of bounded contexts to become hazy. Models from other contexts can leak and bounded contexts can become bloated with logic that belongs elsewhere or overly dependent on others. We need to be vigilant of this and make sure to keep bounded contexts focused and their boundaries solid. Make sure they stay internally cohesive, and split logic into new bounded contexts when needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aggregates&lt;/strong&gt;: Similar to bounded contexts, unregulated growth can cause aggregates to accumulate extraneous logic, expanding them unnecessarily. As we know, the golden rule for aggregates is for them to be only as big as they need to be to include only the data that needs to be strongly consistent for them to be able to fulfill the business requirements. We need to make sure aggregates only contain the logic that&amp;rsquo;s closely related to their purpose in the domain. When necessary, new aggregates can be created for new logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;section-12-domain-driven-design-in-the-real-world&#34;&gt;Section 12: Domain-driven design in the real world&lt;/h3&gt;
&lt;p&gt;Some common misconceptions about DDD are that it can only be applied in greenfield projects where the entire team are DDD experts and that all the tools need to be included wholesale for the practice to be effective. This couldn&amp;rsquo;t be further from the truth. In fact, &lt;strong&gt;brownfield projects that have already proven their business value but have accumulated tech debt are the ones that stand to benefit the most from applying DDD&lt;/strong&gt;. In these situations, the patterns we&amp;rsquo;ve discussed can be gradually introduced, within the context of a greater effort to modernize the code base.&lt;/p&gt;
&lt;h4 id=&#34;strategic-analysis&#34;&gt;Strategic analysis&lt;/h4&gt;
&lt;p&gt;Unsurprisingly, when introducing DDD into an organization, the first step should be to understand its business domain. To this end, key questions ought to be asked:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What is the organization&amp;rsquo;s business domain?&lt;/li&gt;
&lt;li&gt;Who are its customers?&lt;/li&gt;
&lt;li&gt;What service or product does it provide to its customers?&lt;/li&gt;
&lt;li&gt;Who are its competitors?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;ll give us a general understanding of its &lt;strong&gt;business goals&lt;/strong&gt;. Then, a good place to look next is the &lt;strong&gt;organizational structure&lt;/strong&gt;. The divisions, departments, etc. That&amp;rsquo;ll give us insight into the subdomains. Next, we should analyze these subdomains and identify their types.&lt;/p&gt;
&lt;p&gt;The core subdomains will be the areas of &lt;strong&gt;business differentiation and competitive advantage&lt;/strong&gt;. Some intellectual property, patent or algorithm that gives them an edge against their competitors. Unfortunately, you are also likely to find core subdomains in the software that&amp;rsquo;s worst designed. Usually legacy systems that everybody is afraid of refactoring due to their immense business value. For generic subdomains, identify where &lt;strong&gt;off the shelf solutions&lt;/strong&gt; are deployed or &lt;strong&gt;open source components&lt;/strong&gt; are integrated. Finally, supporting subdomains will be whatever is left. Those applications that have been &lt;strong&gt;developed in house but are not particularly interesting&lt;/strong&gt;. Mostly CRUDs and ETLs. They may also be badly designed, but the pain they cause is much less severe because they don&amp;rsquo;t change too often nor do they offer great business value.&lt;/p&gt;
&lt;p&gt;Next, dig deeper and explore the current design. &lt;strong&gt;Identify the high level physical software components&lt;/strong&gt;: applications, services, backend jobs. These likely won&amp;rsquo;t be clearly defined bounded contexts in the DDD sense, but will give you a starting point in moving in that direction for system decomposition. The key here is to identify the components that have decoupled lifecycles. The ones that can be evolved, built and deployed independently. Next, identify the patterns that have been used to implement their interactions with one another, their internal architecture and business logic. Make sure they are appropriate for the level of complexity of the business problems they are addressing.&lt;/p&gt;
&lt;p&gt;Finally, &lt;strong&gt;identify where domain knowledge has been lost&lt;/strong&gt; and endeavor to recover it. As mentioned earlier, the EventStorming workshop is a great exercise to go through for this.&lt;/p&gt;
&lt;h4 id=&#34;modernization-strategy&#34;&gt;Modernization strategy&lt;/h4&gt;
&lt;p&gt;When it comes to modernizing a code base by moving it in the direction of DDD, &lt;strong&gt;it&amp;rsquo;s better to start small and do it gradually&lt;/strong&gt;. &amp;ldquo;Big rewrite&amp;rdquo; projects are rarely successful, and should be avoided. Here are some suggestions to keep in mind:&lt;/p&gt;
&lt;p&gt;When working on a big monolithic system, it&amp;rsquo;s often useful to &lt;strong&gt;start by making its logical boundaries explicit along the lines of the subdomains&lt;/strong&gt; that it contains. Separation into bounded contexts would be the ideal, but it is often too big a task to undertake in one fell swoop. For example, instead of having namespaces and modules reference technical patterns, it is often useful to have them reflect the subdomains. That is, instead of namespaces like &lt;code&gt;Store.WebUI&lt;/code&gt;, &lt;code&gt;Store.Infrastructure&lt;/code&gt; or &lt;code&gt;Store.Services&lt;/code&gt;; that can be reorganized into the likes of &lt;code&gt;Store.Inventory&lt;/code&gt;, &lt;code&gt;Store.OrderProcessing&lt;/code&gt;, &lt;code&gt;Store.ShoppingCart&lt;/code&gt;. That can become the basis for further physical separation down the line. Also remember to apply similar separation to other places where code lives, like stored procedures in the database or serverless functions in a cloud provider.&lt;/p&gt;
&lt;p&gt;When it comes to physical separation of modules, starting small is also advisable. Instead of decomposing a big monolith into many microservices right away, &lt;strong&gt;identify one or two modules that would provide the highest business value&lt;/strong&gt; and extract those. The logical boundaries discussed in the previous paragraph are a good guide, as these can be promoted into physical boundaries.&lt;/p&gt;
&lt;p&gt;For business logic and internal component architecture, &lt;strong&gt;look for the pain&lt;/strong&gt;. The areas of a code base where matters of core subdomains are handled, and which have been implemented using inappropriate patterns for their level of complexity, are usually the ones to attack first, as they will provide the most benefit for being in better shape. These are the areas that produce more pain for developers, where changes are overly expensive. Beware of big jumps though. Refactoring transaction scripts directly into an event sourced domain model is a risky proposition. Tackling the entire codebase all at once is also dangerous. It&amp;rsquo;s better to do it gradually, picking specific use cases or business operations and evolving them one by one, step by step. Continue to avoid a big rewrite, and instead operate in small incremental steps.&lt;/p&gt;
&lt;p&gt;This migration can be done in place, but also, the &lt;a href=&#34;https://martinfowler.com/bliki/StranglerFigApplication.html&#34;&gt;strangler fig pattern&lt;/a&gt; can be a good choice for these. With this pattern, the main idea is to create a new component to replace an old legacy one. The new strangler component grows gradually as more and more features are migrated from the old one, until it becomes obsolete and gets fully replaced. In the period of time in which both old and new components are &amp;ldquo;alive&amp;rdquo;, a facade is put in between them and the rest of the system. This facade takes care of gradually re-routing messages to the new component as they come online. Ultimately, the facade gets removed when the new component is ready to handle all the requests that the legacy one used to support.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/applying-it-in-practice-ddd-part-4/strangler-fig.png&#34; alt=&#34;The strangler fig pattern&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;With the strangler fig pattern, we develop a new component that gradually replaces a legacy one. During the transition, both components work with the same database, and the rest of the system interacts with them through a facade.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;selling-domain-driven-design&#34;&gt;Selling Domain-driven design&lt;/h4&gt;
&lt;p&gt;Finally, don&amp;rsquo;t fall into the trap of thinking that you need to sell your entire organization on DDD in order to start using it. &lt;strong&gt;DDD is first and foremost an engineering practice&lt;/strong&gt;, and as engineers it is up to us to use it. It doesn&amp;rsquo;t need to be an organizational strategy, you can still reap the benefits of DDD by incorporating it into your professional toolbox.&lt;/p&gt;
&lt;p&gt;For example, &lt;strong&gt;you can make the decision to develop and use the ubiquitous language&lt;/strong&gt;. Listen to domain experts and other stakeholders to learn the terms and concepts and make sure the code speaks the same language. Also, use that language yourself in all project communications. Try to identify and resolve inconsistencies. Do this with the help of domain experts, who are often happy to talk to developers, even in informal settings.&lt;/p&gt;
&lt;p&gt;For big systems, explore the options for decomposition &lt;strong&gt;using bounded contexts as guiding principles&lt;/strong&gt;, even if not calling them out by name explicitly. Think of the principles behind the pattern: &amp;ldquo;All-in-one&amp;rdquo; solutions are often ineffective and incur high cognitive load and complexity. So, alleviate this by separating them into decoupled physical components, each with its own model of the domain, geared towards solving specific problems. Remember that high ownership is a desirable trait. To that end, ensure that a given code base is owned by a single team. This way we can avoid friction when the teams cannot collaborate effectively.&lt;/p&gt;
&lt;p&gt;Similarly, when implementing business logic using the domain model patterns we&amp;rsquo;ve seen, &lt;strong&gt;think about the principles behind them&lt;/strong&gt;. About their purpose and advantages when compared to the alternatives. To protect data consistency, you can make transactional boundaries explicit by leveraging aggregates. Aggregates are great places to encapsulate related business logic. This prevents logic from being duplicated, becoming out of sync, and far away from the data structures that it manages. For the same reasons, prefer application logic implemented in aggregates instead of stored procedures. Make sure the aggregates and the transactional boundaries they represent stay small. This keeps aggregate complexity from getting out of hand and also helps with performance. Also, when long term data consistency is a concern, traditional log files fall short. In these cases event sourcing is more reliable.&lt;/p&gt;
&lt;p&gt;At the end of the day, &lt;strong&gt;remember to be pragmatic with DDD&lt;/strong&gt;. It is not an all-or-nothing proposition. Feel free to pick the tools, patterns and practices that will give you the greatest bang for the buck in your particular situation. Even if they are different from the ones we&amp;rsquo;ve discussed so far. The most important thing is to come up with effective models, cultivate a ubiquitous language, and work closely with domain experts. Keep in mind that DDD is not necessarily about value objects, entities and aggregates. DDD is about putting the business domain front and center, analyzing it and letting it drive the design, and coming up with effective models of the domain that can solve specific problems.&lt;/p&gt;
&lt;h3 id=&#34;finally&#34;&gt;Finally&lt;/h3&gt;
&lt;p&gt;And with that we&amp;rsquo;ve reached the end of this series of blog posts discussing domain-driven design. Like I mentioned at the beginning, this series is greatly inspired by &lt;a href=&#34;https://vladikk.com/&#34;&gt;Vlad Khononov&lt;/a&gt;&amp;rsquo;s book: &amp;ldquo;&lt;a href=&#34;https://www.oreilly.com/library/view/learning-domain-driven-design/9781098100124/&#34;&gt;Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy&lt;/a&gt;&amp;rdquo;. This book does a great job in democratizing DDD, as Evans&amp;rsquo; original work can be somewhat dense at times. And while I went through a lot of the topics that he covers in his book, there are many more that I didn&amp;rsquo;t, like EventStorming, microservices, event driven architectures, data warehouses, lakes and meshes, and their relationship with DDD. So I can&amp;rsquo;t recommend strongly enough that you pick it up yourself. It is a great addition to the shelf of any software engineering professional. My hope is that this series has served as a solid introduction and encourages readers to dig deeper into the topic.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Designing software architecture with Domain-Driven Design</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2026/05/designing-software-architecture-ddd-part-3/"/>
      <id>https://www.endpointdev.com/blog/2026/05/designing-software-architecture-ddd-part-3/</id>
      <published>2026-05-05T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/cover.webp&#34; alt=&#34;A panoramic view from a mountain overlooks a vast valley stretching to the horizon under a stormy sky.&#34;&gt;&lt;br&gt;
Photo by Juan Pablo Ventoso, 2023.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is part 3 of a series of blog posts on Domain-Driven Design:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/&#34;&gt;High level system analysis and design with Domain-Driven Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/04/implementing-business-logic-ddd-part-2/&#34;&gt;Implementing business logic with Domain-Driven Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/&#34;&gt;Designing software architecture with Domain-Driven Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/05/applying-it-in-practice-ddd-part-4/&#34;&gt;Applying Domain-Driven Design in Practice&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Domain-Driven Design&lt;/strong&gt; is an approach to software development that focuses on, &lt;a href=&#34;https://www.oreilly.com/library/view/domain-driven-design-tackling/0321125215/&#34;&gt;as Eric Evans puts it&lt;/a&gt;, &amp;ldquo;tackling the complexity in the heart of software&amp;rdquo;. And what is in the heart of software? The business domain in which it operates. Or more specifically: a &lt;strong&gt;model&lt;/strong&gt; of it, made of code. That is, the code that implements the business logic that comes into play when solving problems within the realm of a particular business activity.&lt;/p&gt;
&lt;p&gt;DDD is not just about writing code though. It&amp;rsquo;s a whole methodology that touches on business needs, requirements gathering, organizational dynamics, high level architectural design, and lower level patterns for implementing software intensive systems.&lt;/p&gt;
&lt;p&gt;As a result, DDD offers a treasure trove of concepts, patterns and tools that can be applied to any software project, regardless of the size and complexity.&lt;/p&gt;
&lt;p&gt;In this series of blog posts we&amp;rsquo;re going to explore many aspects of DDD. We will be following the structure laid out by &lt;a href=&#34;https://vladikk.com/&#34;&gt;Vlad Khononov&lt;/a&gt;&amp;rsquo;s excellent book on the topic &amp;ldquo;&lt;a href=&#34;https://www.oreilly.com/library/view/learning-domain-driven-design/9781098100124/&#34;&gt;Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy&lt;/a&gt;&amp;rdquo;. So you can think of this series as a summary of that book. An abridged version that can serve as a review for anybody who has read it; but also as an entry point for people who are new to DDD.&lt;/p&gt;
&lt;h3 id=&#34;table-of-contents&#34;&gt;Table of contents&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#designing-software-architecture-with-domain-driven-design&#34;&gt;Designing software architecture with Domain-Driven Design&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#table-of-contents&#34;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-8-architectural-patterns&#34;&gt;Section 8: Architectural patterns&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#layered-architecture&#34;&gt;Layered architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#ports-and-adapters&#34;&gt;Ports and adapters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#command-query-responsibility-segregation&#34;&gt;Command query responsibility segregation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#scope&#34;&gt;Scope&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-9-communication-patterns&#34;&gt;Section 9: Communication patterns&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#model-translation&#34;&gt;Model translation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#stateless-model-translation&#34;&gt;Stateless model translation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#stateful-model-translation&#34;&gt;Stateful model translation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#integrating-aggregates&#34;&gt;Integrating aggregates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#outbox&#34;&gt;Outbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#saga&#34;&gt;Saga&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#process-manager&#34;&gt;Process manager&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#architectural-patterns-to-use-within-bounded-contexts&#34;&gt;Architectural patterns to use within bounded contexts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#patterns-for-communicating-across-bounded-contexts&#34;&gt;Patterns for communicating across bounded contexts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;section-8-architectural-patterns&#34;&gt;Section 8: Architectural patterns&lt;/h3&gt;
&lt;p&gt;Now that we&amp;rsquo;ve seen various patterns for implementing business logic, i.e. &amp;ldquo;the heart of software&amp;rdquo;. We turn our attention to architecture.&lt;/p&gt;
&lt;p&gt;Indeed, the business logic is the raison d&amp;rsquo;être for a software application. But applications have other responsibilities that are also important. Like interacting with users, receiving requests and returning results, storing data, interfacing with external services. In order to balance all these concerns and make sure the code base does not devolve into an unmaintainable big ball of mud, we need to be intentional in how we organize it. We need to design its architecture.&lt;/p&gt;
&lt;p&gt;That is, the rules and principles that we follow to organize the various aspects of the code base and create clear boundaries between them. In essence, software architecture is about defining a system&amp;rsquo;s big logical components, their dependencies and interactions.&lt;/p&gt;
&lt;p&gt;In this section we will see three common architectural patterns: layered architecture, ports and adapters, and command query responsibility segregation.&lt;/p&gt;
&lt;h4 id=&#34;layered-architecture&#34;&gt;Layered architecture&lt;/h4&gt;
&lt;p&gt;The &lt;strong&gt;layered architecture&lt;/strong&gt; is one of the most common architectural patterns out there. It has been present, in one form or another, for a long time. The main idea of the pattern is to separate applications into three layers: the presentation layer, the business logic layer, and the data access layer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/layered-architecture.png&#34; alt=&#34;Layered architecture&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Layered architecture.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;presentation layer&lt;/strong&gt; (or user interface layer) is meant to implement the mechanisms through which consumers interact with the application. This means the app&amp;rsquo;s graphical user interface (GUI), command line interface (CLI) or application programming interface (API).&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;business logic layer&lt;/strong&gt; (or domain layer) implements the business rules, validation and invariants. This is where we&amp;rsquo;d implement the patterns that we&amp;rsquo;ve seen so far like transaction script, active record, or a domain model.&lt;/p&gt;
&lt;p&gt;Sometimes, an additional &amp;ldquo;&lt;strong&gt;service layer&lt;/strong&gt;&amp;rdquo; or &amp;ldquo;&lt;strong&gt;application layer&lt;/strong&gt;&amp;rdquo; emerges between the presentation and business logic layers. When the domain logic needs some level of orchestration, such as it is the case with active records and domain models, it is often useful to further separate the presentation and business logic layers by exposing a sort of &amp;ldquo;public interface&amp;rdquo; to the domain. That is, a series of procedures (i.e. transaction scripts) that serve as a facade that maps presentation layer actions (e.g. user interactions) to business domain transactions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/layered-architecture-with-service-layer.png&#34; alt=&#34;Layered architecture with service layer&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The service layer sits between the presentation and business logic layers. It implements a series of actions which map closely to the operations exposed to users by the presentation layer.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example of refactoring a &amp;ldquo;fat&amp;rdquo; controller by introducing a service layer:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// This is an MVC controller that implements a REST API endpoint for adding&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// items to a shopping cart. It lives in the application&amp;#39;s presentation layer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// and leverages business logic layer components to run its logic.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MvcEcommerce.WebApi.Controllers&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;[Route(&amp;#34;api/[controller]&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;)]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;[ApiController]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;QuoteItemsController&lt;/span&gt; : ControllerBase
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// This action takes care of processing user input, fetching HTTP context&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// values like cookies, responding with proper HTTP status codes and&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// orchestrating the business logic.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;    [HttpPost]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; Task&amp;lt;ActionResult&amp;gt; Post([FromBody] QuoteItemPost payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; quoteId = _quoteCookieManager.GetQuoteIdFromCookie(Request);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; quote = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; _quoteRepository.FindOpenByIdAsync(quoteId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (quote == &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; NotFound(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Quote not found&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; product = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; _productRepository.FindByIdAsync(payload.ProductId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (product == &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; NotFound(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Product not found&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; matchingQuoteItem = quote.GetItemBy(productId: payload.ProductId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (matchingQuoteItem != &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; BadRequest(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Item already exists&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; quoteItem = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; QuoteItem()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            Product = product,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            Quantity = payload.Quantity,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        quote.Items.Add(quoteItem);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; _quoteRepository.UpdateAsync(quote);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Ok(quoteItem);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This controller does too much. We can move a lot of its logic into a service layer component:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MvcEcommerce.WebApi.Controllers&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;[Route(&amp;#34;api/[controller]&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;)]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;[ApiController]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;QuoteItemsController&lt;/span&gt; : ControllerBase
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// After refactoring, this action is now much simpler and only concerned&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// with presentation layer issues. That is, handling user input and the HTTP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// specific aspects of request processing. It relies on the service layer to&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// orchestrate the business logic.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;    [HttpPost]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; Task&amp;lt;ActionResult&amp;gt; Post([FromBody] QuoteItemPost quoteItem)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; quoteId = _quoteCookieManager.GetQuoteIdFromCookie(Request);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; result = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; _quoteItemCreator.Run(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                QuoteId = quoteId.Value,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ProductId = quoteItem.ProductId,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                Quantity = quoteItem.Quantity
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Ok(result);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;catch&lt;/span&gt; (EntityNotFoundException ex)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; NotFound(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; ErrorMessage(ex));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;catch&lt;/span&gt; (DomainException ex)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; BadRequest(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; ErrorMessage(ex));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// This service object implements all the domain logic orchestration that&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// used to live in the controller. Notice how this code is not concerned with&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// presentation layer responsibilities like handling HTTP specific logic, for&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// example.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MvcEcommerce.ServiceLayer.Services&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;QuoteItemCreator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; Task&amp;lt;QuoteItem&amp;gt; Run(InputPayload payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; product =
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; _productRepository.FindByIdAsync(payload.ProductId) ??
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; EntityNotFoundException(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Product not found&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; quote =
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; _quoteRepository.FindOpenByIdAsync(payload.QuoteId) ??
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; EntityNotFoundException(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Quote not found&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; matchingQuoteItem = quote.GetItemBy(productId: payload.ProductId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (matchingQuoteItem != &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; DomainException(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Item already exists&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; quoteItem = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; QuoteItem()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            Product = product,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            Quantity = payload.Quantity,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        quote.Items.Add(quoteItem);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; _quoteRepository.UpdateAsync(quote);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; quoteItem;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, the &lt;strong&gt;data access layer&lt;/strong&gt; is meant to provide the means of interacting with persistent storage like databases, search indexes, file systems, etc. In modern systems, this layer has evolved into more of a &amp;ldquo;infrastructure&amp;rdquo; layer, and taken on the responsibility of interacting with external APIs and other kinds of web services. So, not strictly limited to pure &amp;ldquo;data storage&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;The communication between these layers is one-way, from top to bottom. Meaning that the presentation layer holds a reference to, depends on, and calls to the business logic layer. Same with the business logic layer to the data access layer.&lt;/p&gt;
&lt;p&gt;This communication pattern is excellent for active record and transaction script based systems. For domain models, it begins to fall a bit short. This is because the business logic depending on the data access logic contradicts one of the core principles of domain models: the fact that they are supposed to be plain old objects, with no dependencies on frameworks or infrastructure.&lt;/p&gt;
&lt;h4 id=&#34;ports-and-adapters&#34;&gt;Ports and adapters&lt;/h4&gt;
&lt;p&gt;The &lt;strong&gt;ports and adapters architecture&lt;/strong&gt; leverages the dependency inversion principle to address the shortcomings of the traditional layered architecture and make it ideal for implementing domain models. Its main advantage is that it decouples the business logic layer from the infrastructure.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://en.wikipedia.org/wiki/Dependency_inversion_principle&#34;&gt;&lt;strong&gt;dependency inversion principle&lt;/strong&gt;&lt;/a&gt; dictates that, instead of higher level components directly depending on and referencing lower level ones; it is the lower level components that should depend on the higher level ones. This is done by the higher level components defining contracts for the lower level components to implement, and through those, be integrated into the higher level components&amp;rsquo; workflows. The higher level components only ever interact with the lower level ones through the contracts that they themselves define.&lt;/p&gt;
&lt;p&gt;Case in point: instead of the business logic depending on data access logic, like in the layered architecture; the business logic layer takes center stage and defines the contracts that the data access layer (and really, all infrastructure) must abide to in order to be usable to the business logic.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s precisely where the ports and adapters name comes from. The business logic defines contracts/interfaces, AKA &amp;ldquo;ports&amp;rdquo;; and the infrastructure provides concrete implementations for these interfaces which can talk to external components: the &amp;ldquo;adapters&amp;rdquo;. Then, application bootstrapping logic, or &lt;a href=&#34;https://en.wikipedia.org/wiki/Dependency_injection&#34;&gt;&lt;strong&gt;dependency injection&lt;/strong&gt;&lt;/a&gt;, takes care of supplying the concrete objects (or adapters) to the abstract interfaces (or ports) that the business logic specifies. This is exactly what a domain model calls for.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example of a minimal, thin vertical slice of an application designed using the ports and adapters pattern:&lt;/p&gt;
&lt;p&gt;The business logic implements some procedure which necessitates interacting with the database, an email delivery service, and a payment processor:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Ecommerce.BusinessLogicLayer.Services&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;OrderCreator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// This service does not directly depend on concrete classes. Instead, it&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// references abstract interfaces. The concrete implementations are given&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// via dependency injection through the constructor.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;readonly&lt;/span&gt; IOrderRepository _orderRepository;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;readonly&lt;/span&gt; IOrderConfirmationMailer _orderConfirmationMailer;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;readonly&lt;/span&gt; IPaymentGateway _paymentGateway;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; OrderCreator(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        IOrderRepository orderRepository,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        IOrderConfirmationMailer orderConfirmationMailer,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        IPaymentGateway paymentGateway
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _orderRepository = orderRepository;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _orderConfirmationMailer = orderConfirmationMailer;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _paymentGateway = paymentGateway;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; Task&amp;lt;Order&amp;gt; Run(InputPayload payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; order = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Order(payload);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; result = _paymentGateway.SubmitPayment(order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (!result.IsSuccess)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; DomainException(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Error processing payment&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; _orderRepository.Save(order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; _orderConfirmationMailer.Send(order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; order;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The business logic layer also defines its ports. That is, interfaces that the infrastructure layer will have to implement:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Ecommerce.BusinessLogicLayer.Interfaces&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;IPaymentGateway&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PaymentTransactionResult SubmitPayment(Order order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;IOrderRepository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Task&amp;lt;Order&amp;gt; Save(Order Order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;IOrderConfirmationMailer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Task Send(Order order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The infrastructure layer provides implementations for these interfaces:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Ecommerce.InfrastructureLayer.Payments&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;AuthorizeNetPaymentGateway&lt;/span&gt; : IPaymentGateway
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; AuthorizeNetPaymentGateway(&lt;span style=&#34;color:#888&#34;&gt;/* ... */&lt;/span&gt;) { &lt;span style=&#34;color:#888&#34;&gt;/* ... */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; PaymentTransactionResult SubmitPayment(Order order) { &lt;span style=&#34;color:#888&#34;&gt;/* ... */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Ecommerce.InfrastructureLayer.Repositories&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;OrderRepository&lt;/span&gt; : IOrderRepository
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; OrderRepository(&lt;span style=&#34;color:#888&#34;&gt;/* ... */&lt;/span&gt;) { &lt;span style=&#34;color:#888&#34;&gt;/* ... */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; Task&amp;lt;Order&amp;gt; Save(Order Order) { &lt;span style=&#34;color:#888&#34;&gt;/* ... */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Ecommerce.InfrastructureLayer.Mailers&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;OrderConfirmationAwsSesMailer&lt;/span&gt; : IOrderConfirmationMailer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; OrderConfirmationAwsSesMailer(&lt;span style=&#34;color:#888&#34;&gt;/* ... */&lt;/span&gt;) { &lt;span style=&#34;color:#888&#34;&gt;/* ... */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; Task Send(Order order) { &lt;span style=&#34;color:#888&#34;&gt;/* ... */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Like mentioned before, everything can be wired together via dependency injection or some other form of bootstrapping. Most frameworks have their own way of resolving dependencies and instantiating service objects like these; in order to execute them as a result of requests from consumers (e.g. a CLI command, a web request, the click of a button). In essence, it&amp;rsquo;s something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; repository = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; OrderRepository(dbContext);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; mailer = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; OrderConfirmationAwsSesMailer(mailerConfig);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; paymentGateway = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; AuthorizeNetPaymentGateway(paymentConfig);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; orderCreator = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; OrderCreator(repository, mailer, paymentGateway);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;orderCreator.Run(payload);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, the ports and adapters architectural pattern has a &lt;strong&gt;business logic layer&lt;/strong&gt; which has no dependencies on any other components outside of itself. It defines a set of interfaces for all external components that want to interact with it. It also has an &lt;strong&gt;infrastructure layer&lt;/strong&gt; which implements concrete classes for the domain layer&amp;rsquo;s interfaces. Data access, interaction with external services, user interface, and presentation logic all live here. And finally, it has a &lt;strong&gt;service/application layer&lt;/strong&gt; which, similar to its counterpart from the layered architecture, can emerge between the business logic and infrastructure layers when needed to expose a set of procedures that closely map to user interface actions. It exposes all the business operations that the system supports and orchestrates the business logic to carry them out.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/ports-and-adapters.png&#34; alt=&#34;The ports and adapters architectural pattern&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;When organizing the code following the the ports and adapters architectural pattern, the business logic layer defines interfaces/ports, which the infrastructure layer implements concrete objects for. These objects take care of interacting with the external world.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Clean architecture, onion architecture and hexagonal architecture are all different names for the same core concepts and principles espoused by ports and adapters; sometimes with slight variations depending on the particular flavor and tech stack.&lt;/p&gt;
&lt;h4 id=&#34;command-query-responsibility-segregation&#34;&gt;Command query responsibility segregation&lt;/h4&gt;
&lt;p&gt;The &lt;strong&gt;command query responsibility segregation pattern&lt;/strong&gt; (CQRS) builds on the principles from ports and adapters and adds support for multiple different representations of the system&amp;rsquo;s data. That is, having multiple persistence models for the same data set. One of the most common examples is a system that stores and operates on day to day business transactions using an OLTP (&lt;a href=&#34;https://en.wikipedia.org/wiki/Online_transaction_processing&#34;&gt;online transaction processing&lt;/a&gt;) representation, but also needs to provide an OLAP (&lt;a href=&#34;https://en.wikipedia.org/wiki/Online_analytical_processing&#34;&gt;online analytical processing&lt;/a&gt;) data warehouse for high level business analysis. One ground-truth source of data (the OLTP) is used to produce additional representations with a different schema (the OLAP). CQRS enables this.&lt;/p&gt;
&lt;p&gt;As such, CQRS is ideal for event sourced domain models, because it allows persisting the many projections of an aggregate&amp;rsquo;s data into their own databases. (Remember that in the context of event sourcing, a projection is a representation of the state of a business entity which is constructed based on its stored domain events). It allows of course, querying of these projections with much more flexibility than what a pure event store allows on its own.&lt;/p&gt;
&lt;p&gt;At the core of CQRS there are two types of models: a command execution model (the C in CQRS) and one or many read, or query, models (the Q in CQRS). In database terms, this is similar to a &lt;a href=&#34;https://en.wikipedia.org/wiki/Master%E2%80%93slave_(technology)&#34;&gt;&lt;strong&gt;primary-replica&lt;/strong&gt;&lt;/a&gt; type of situation, where the command execution model represents the primary, and the read models represent the replicas.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/cqrs.png&#34; alt=&#34;Command query responsibility segregation&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;CQRS exposes two types of models, one for executing commands and many others for reading. In the backend, a projection engine keeps the read models up to date with the latest changes from the command execution model.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;command execution model&lt;/strong&gt; is the system&amp;rsquo;s source of truth, whose data is strongly consistent. It&amp;rsquo;s the one used to execute and record business operations, and enforce business rules and invariants. All operations that result in changes to the system state are handled here.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;query, or read, models&lt;/strong&gt; take care of exposing different projections of the system state. These are read-only models that are meant for presenting the system&amp;rsquo;s data to its consumers. They are generated based on the main data source, which is the data maintained in the command execution model. In fact, read models should be capable of being easily destroyed and recreated from the main data.&lt;/p&gt;
&lt;p&gt;Read models are generated using components that we call &lt;strong&gt;projection engines&lt;/strong&gt;, which can work synchronously or asynchronously when fetching data from the command execution model to generate their projections.&lt;/p&gt;
&lt;p&gt;Synchronously, projection engines generate read models using a &lt;strong&gt;catch-up subscription&lt;/strong&gt; design. It works like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The projection engine determines the last query checkpoint. Basically, when was the last time it read the main database.&lt;/li&gt;
&lt;li&gt;The projection engine queries the main database and identifies newly added and updated records since the last query checkpoint.&lt;/li&gt;
&lt;li&gt;The projection engine uses the new data to regenerate or update the read model.&lt;/li&gt;
&lt;li&gt;The projection engine updates the latest query checkpoint, to be used during the next execution of the engine, starting again at step 1.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/synchronous-projection-engine.png&#34; alt=&#34;Synchronous projection engine&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Synchronous projection engine.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This checkpoint can be implemented in various ways. The main idea is to offer a mechanism for the projection engine to be able to tell which records are new or have changed since its last run. One way to do it is using something akin to SQL Server&amp;rsquo;s &lt;a href=&#34;https://learn.microsoft.com/en-us/sql/t-sql/data-types/rowversion-transact-sql?view=sql-server-ver17&#34;&gt;&lt;strong&gt;rowversion&lt;/strong&gt;&lt;/a&gt; feature. This is a database-wide, auto-incrementing numeric value that increases after every INSERT and UPDATE operation. The newly incremented value is assigned to the rows that were added or updated. With this, the projection engine can easily query all records whose rowversion is greater than the last checkpoint. Similar functionality can be implemented using database triggers too. Or database tables can also include &lt;strong&gt;timestamp&lt;/strong&gt; fields that indicate when records were last touched.&lt;/p&gt;
&lt;p&gt;Asynchronous projection engines on the other hand, rely on the command execution model publishing all changes to a &lt;strong&gt;message bus&lt;/strong&gt;. The projection engine can subscribe to these messages and update its read model as they come. This method, while scalable, comes with drawbacks inherent to distributed computing like handling duplicates and out of order messages. It&amp;rsquo;s also more difficult to destroy and regenerate read models that rely on asynchronous messaging only, since the messages are gone after they are processed. Often the better solution is to use a synchronous design and then, only if needed, augment it with the asynchrony.&lt;/p&gt;
&lt;p&gt;Naturally, CQRS is ideal when the system needs to support different types of databases. Imagine an online store, for example, which has an inventory management component that works directly with the command execution model backed by a relational database. But for displaying the product catalog to users, it uses a read model backed by a search index, optimized for full text search. And for event sourced domain models, CQRS is practically mandatory.&lt;/p&gt;
&lt;h4 id=&#34;scope&#34;&gt;Scope&lt;/h4&gt;
&lt;p&gt;The patterns that we&amp;rsquo;ve seen in this section are not exclusively meant as system-wide nor even bounded-context-wide organizational principles. Arbitrarily enforcing a single pattern everywhere often leads to accidental complexity. Instead we should follow DDD&amp;rsquo;s core principles and deploy these strategies according to what the business domain necessitates. Indeed, within a bounded context, especially one that deals with many subdomains, there should be logical separation between these subdomains. Each resulting module could use a different architectural pattern. In other words, these patterns divide the code base into horizontal slices; and the subdomains can be used to define the vertical slices. This way, a monolithic bounded context can be modularized. This leaves the code base in a good position for future refactoring and further physical separation into distinct bounded contexts in the future (i.e. into separate applications, services and/or processes).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/architectural-slices.png&#34; alt=&#34;Architectural slices&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;When needed, different architectural patterns can be deployed to different subdomains within the same bounded context.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&#34;section-9-communication-patterns&#34;&gt;Section 9: Communication patterns&lt;/h3&gt;
&lt;p&gt;In the last few sections we&amp;rsquo;ve discussed how to implement business logic and how to leverage architectural patterns to organize the code within a bounded context. In this section, we will take a higher level view and go beyond the scope of a single bounded context. We will learn about patterns of communication across bounded contexts. In other words, how to integrate them.&lt;/p&gt;
&lt;h4 id=&#34;model-translation&#34;&gt;Model translation&lt;/h4&gt;
&lt;p&gt;Back when we talked about integrating bounded contexts, we discussed how they can use different communication strategies depending on the disposition of the teams that own them. When the teams have strong communication, &lt;strong&gt;cooperation&lt;/strong&gt; patterns like &lt;strong&gt;partnership&lt;/strong&gt; and &lt;strong&gt;shared kernel&lt;/strong&gt; can be used for integration. Here, bounded contexts can communicate without friction. The protocols for doing so are defined by the teams in an ad-hoc manner and development moves forward without too many issues.&lt;/p&gt;
&lt;p&gt;However, when the teams are not closely aligned, and cooperation patterns are impossible, a &lt;strong&gt;customer-supplier&lt;/strong&gt; relationship emerges. This can be addressed by implementing an &lt;strong&gt;anticorruption layer&lt;/strong&gt; in the downstream consumer, to adapt the upstream supplier&amp;rsquo;s model to the consumer&amp;rsquo;s needs. Another option is for the upstream supplier to implement an &lt;strong&gt;open-host service&lt;/strong&gt; and expose an integration-specific &lt;strong&gt;published language&lt;/strong&gt;, which isolates consumers from the details of its internal model. Both approaches are meant to protect a bounded context&amp;rsquo;s model from the influence of external models.&lt;/p&gt;
&lt;p&gt;In these non-cooperation cases, we have to be more intentional in how we design the communication between bounded contexts, and the need for a translation of their models/languages arises. This logic that translates the models can either be stateless or stateful.&lt;/p&gt;
&lt;h4 id=&#34;stateless-model-translation&#34;&gt;Stateless model translation&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Stateless model translation&lt;/strong&gt; can happen on the fly via the &lt;a href=&#34;https://refactoring.guru/design-patterns/proxy&#34;&gt;&lt;strong&gt;proxy design pattern&lt;/strong&gt;&lt;/a&gt;. The idea is to put an intermediary component in place, which receives messages in one language, translates them, and forwards them to their destination.&lt;/p&gt;
&lt;p&gt;If the proxy is implemented by the consumer or downstream component, functioning as an anticorruption layer, then it intercepts outgoing requests and translates them to a language that the upstream supplier can understand. It does the same with the incoming responses from upstream: it translates them to a language that the consumer can understand.&lt;/p&gt;
&lt;p&gt;If the proxy is implemented by the supplier or upstream component, it functions as an open-host service. It takes the incoming requests that arrive using the published language and translates them to the supplier&amp;rsquo;s internal one. Then, outgoing responses get translated to the published language.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/synchronous-proxy.png&#34; alt=&#34;Synchronous proxy&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;In essence, a proxy is nothing more than a component that sits between two other components and translates the messages passing between them.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;These proxies can be synchronous or asynchronous.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Synchronous&lt;/strong&gt; translation is straightforward. Messages are translated just as they are being received or sent, depending on whether the translation happens upstream or downstream. Normally, the translation logic is implemented directly in the bounded context that needs it.&lt;/p&gt;
&lt;p&gt;In some cases though, it makes sense to separate it into its own independent component, implementing an &lt;a href=&#34;https://microservices.io/patterns/apigateway.html&#34;&gt;&lt;strong&gt;API gateway&lt;/strong&gt;&lt;/a&gt; pattern. Sometimes, off the shelf software or cloud services like &lt;a href=&#34;https://www.krakend.io/&#34;&gt;KrakenD&lt;/a&gt; or &lt;a href=&#34;https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html&#34;&gt;AWS API Gateway&lt;/a&gt; can be used to implement them.&lt;/p&gt;
&lt;p&gt;Having a separate API gateway has some advantages, other than the obvious decoupling of model translation logic from actual business logic. It can make it easier to &lt;a href=&#34;https://restfulapi.net/versioning/&#34;&gt;expose multiple versions&lt;/a&gt; of the API. That is, of the published language. It can also be consumed by multiple downstream components, making it essentially an integration-specific bounded context. We call these, &lt;strong&gt;interchange contexts&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/api-gateway.png&#34; alt=&#34;API Gateway&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;When extracted into its own component, a proxy becomes an API gateway. This is useful for further separation of concerns, offering multiple versions of the API&amp;rsquo;s published language, and serving multiple consumers.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Asynchronous&lt;/strong&gt; translation on the other hand, is not direct. It relies on an event driven design to implement a &lt;strong&gt;message proxy&lt;/strong&gt;; which subscribes to events published by one bounded context, translates them, and forwards them to their destination.&lt;/p&gt;
&lt;p&gt;The proxy can also apply filtering to the messages, deciding which ones to forward and which ones to ignore. This is useful, for example, to keep the published language free of domain events that are meant to be internal to the bounded context that produces them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/asynchronous-proxy.png&#34; alt=&#34;Asynchronous proxy&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;An asynchronous proxy listens to events in one language and translates them to another language.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;stateful-model-translation&#34;&gt;Stateful model translation&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Stateful model translation&lt;/strong&gt; comes into play when more complex translation logic that requires persistent storage is needed. This is useful for two common scenarios: when the requests need to be aggregated, and when data from multiple sources needs to be unified. In both cases the component doing the translation needs to keep track of the incoming requests and messages. It stores them in a database in order to reconstruct them eventually.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data aggregation&lt;/strong&gt; may be needed for performance reasons when, for example, multiple requests need to be collected together and dispatched as one batch. Another case is when multiple disparate messages need to be combined into one single bigger message that conveys more complete information. This can work both synchronously and asynchronously. That is, the messages can be direct requests or published events.&lt;/p&gt;
&lt;p&gt;An API gateway on its own is not appropriate for this type of model translation, as it doesn&amp;rsquo;t provide persistent storage or more complex processing logic beyond mapping and rerouting. Instead, it can be implemented from scratch as a bespoke solution, or leveraging off the shelf stream processing platforms like &lt;a href=&#34;https://kafka.apache.org/&#34;&gt;Apache Kafka&lt;/a&gt; or &lt;a href=&#34;https://aws.amazon.com/kinesis/&#34;&gt;AWS Kinesis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/stateful-proxy.png&#34; alt=&#34;Stateful model translation&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A proxy can aggregate and/or combine multiple separate messages into a single one for performance and consolidation purposes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;unification of multiple data sources&lt;/strong&gt; is also a common use case for stateful model translation. This is necessary for example when a component needs to process data from many different upstream suppliers and apply complex business logic to it. This is commonly seen in the &lt;a href=&#34;https://samnewman.io/patterns/architectural/bff/&#34;&gt;&lt;strong&gt;backend for frontend&lt;/strong&gt;&lt;/a&gt; pattern. In this pattern, an API is defined to meet all of a frontend&amp;rsquo;s needs, while serving as a facade for a number of backend services. This allows the frontend to call a single backend to interact with all the various services it may need.&lt;/p&gt;
&lt;h4 id=&#34;integrating-aggregates&#34;&gt;Integrating aggregates&lt;/h4&gt;
&lt;p&gt;Before, we talked about the rules that we follow to limit the scope of aggregates and ensure they encapsulate a coherent set of business logic. We strive to keep them focused, with tight boundaries. They represent strong data consistency and transactional boundaries. That is, they should only contain strongly consistent data and the system&amp;rsquo;s transactions should be limited to a single aggregate instance. It is also true however, that there are business processes that involve multiple aggregates. The following patterns allow us to implement such business processes without breaking the aggregates&amp;rsquo; isolation rules.&lt;/p&gt;
&lt;h4 id=&#34;outbox&#34;&gt;Outbox&lt;/h4&gt;
&lt;p&gt;As we know, along with commands, domain events are part of an aggregate&amp;rsquo;s public interface. By publishing domain events to a message bus, aggregates communicate with the outside world. Or more specifically, to their subscribers, who listen to these events in order to trigger their own logic. The outbox pattern allows aggregates to reliably publish domain events.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;outbox&lt;/strong&gt; pattern works by storing domain events in the same database as the aggregate&amp;rsquo;s data. Leveraging database transactions to ensure both the state changes and the events are saved atomically. The process is this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Aggregate state changes and new events are committed in the same database transaction.&lt;/li&gt;
&lt;li&gt;The message relay picks up the new events from the database.&lt;/li&gt;
&lt;li&gt;The message relay publishes the new events to the message bus.&lt;/li&gt;
&lt;li&gt;After successful publishing, the message relay marks the event as such, or deletes it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/outbox-pattern.png&#34; alt=&#34;The outbox pattern&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The outbox pattern is about storing an aggregate&amp;rsquo;s domain events in a database, making sure to commit them in the same transaction as state changes.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;saga&#34;&gt;Saga&lt;/h4&gt;
&lt;p&gt;A &lt;strong&gt;saga&lt;/strong&gt; leverages the reliable publishing of domain events afforded to us by the outbox pattern to implement business processes that span multiple aggregates. They do this by listening to events and responding to them by issuing commands to relevant components. A saga represents a long-running business process. One that spans multiple transactions.&lt;/p&gt;
&lt;p&gt;Sagas can vary in complexity. Sometimes it is enough to respond to events by directly issuing commands from the saga itself.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;OrderProcessingSaga&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;readonly&lt;/span&gt; IOrderRepository _orderRepository;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;readonly&lt;/span&gt; IPaymentService _paymentService;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;readonly&lt;/span&gt; IInventoryService _inventoryService;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;readonly&lt;/span&gt; IShippingService _shippingService;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;//...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// These event handler methods all follow a similar pattern:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Receive the event, then call the corresponding command.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Process(OrderCaptured @event)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; order = _orderRepository.Fetch(@event.OrderId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _paymentService.ProcessPayment(order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Process(PaymentAccepted @event)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; order = _orderRepository.Fetch(@event.OrderId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _inventoryService.PreparePackage(order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        order.UpdateStatus(@event.NewOrderStatus);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _orderRepository.Save(order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Process(InventoryCleared @event)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; order = _orderRepository.Fetch(@event.OrderId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _shippingService.ScheduleDelivery(order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        order.UpdateStatus(@event.NewOrderStatus);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _orderRepository.Save(order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Process(ShipmentDispatched @event)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; order = _orderRepository.Fetch(@event.OrderId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        order.UpdateStatus(@event.NewOrderStatus);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _orderRepository.Save(order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Other times, a saga needs to keep its own state. For example, when the saga needs to track received events; to react to certain failure conditions; issue retries, etc. In these cases, the saga can be implemented as an event sourced aggregate, keep a record of all received events, and publish events of its own that contain the commands that it wants to execute. Then, a separate message relay component should be listening to the saga&amp;rsquo;s events and run the commands contained within.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;OrderProcessingSaga&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Like any other event sourced aggregate, this saga stores its list of&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// events.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;readonly&lt;/span&gt; IList&amp;lt;DomainEvent&amp;gt; _events = [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// These event handlers all also follow a similar pattern. Instead of&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// directly calling the commands though, they register an event with the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// necessary information for the message relay to call the commands.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Process(OrderCaptured @event)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(@event);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; CommandIssuedEvent(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            target: Target.PaymentService,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; ProcessPaymentCommand(@event.OrderId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Process(PaymentAccepted @event)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(@event);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; CommandIssuedEvent(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            target: Target.InventoryService,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; PreparePackageCommand(@event.OrderId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Process(InventoryCleared @event)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(@event);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; CommandIssuedEvent(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            target: Target.ShippingService,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; ScheduleDeliveryCommand(@event.OrderId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Process(ShipmentDispatched @event)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(@event);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/saga.png&#34; alt=&#34;The saga pattern&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Sagas represent a long running business process. One that spans many transactions. They receive events and invoke commands as a result.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Always remember though, sagas make it possible to implement business processes that span multiple aggregates, but the data across aggregates is only eventually consistent. The core aggregate design rules still apply: only the data within an aggregate is strongly consistent.&lt;/p&gt;
&lt;h4 id=&#34;process-manager&#34;&gt;Process manager&lt;/h4&gt;
&lt;p&gt;The &lt;strong&gt;process manager&lt;/strong&gt; pattern is essentially a more complex saga. While sagas mostly deal with linear flows, and direct mapping between events and commands; a process manager implements more complex business logic that keeps track of the sequence of events, has its own state, and determines how to react. It does not only have a simple mapping of events to commands. Instead it has complex logic that decides how to respond to incoming events. It is a central processing unit, listening to events from and issuing commands to multiple sources, and managing an overall business process.&lt;/p&gt;
&lt;p&gt;Another difference between them is that sagas are instantiated implicitly, while process managers are instantiated explicitly. Meaning that sagas live and die within the context of the events that they listen to. They &amp;ldquo;get activated&amp;rdquo; when an event that they are interested in is published. Process managers on the other hand get activated when the business process they manage gets initiated. They stay alive through the duration of the process&amp;rsquo;s many steps.&lt;/p&gt;
&lt;p&gt;Implementation wise, they are essentially aggregates with a strong focus on responding to events. They have their own persistent state, explicit identity, lifecycle, and related objects; as well as a series of event handlers, just like a saga would have.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/process-manager.png&#34; alt=&#34;Process manager&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A process manager orchestrates a long running business process with complex logic that involves many components. The application layer instantiates them explicitly, as it would any other aggregate.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;OrderProcessManager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;readonly&lt;/span&gt; IList&amp;lt;DomainEvent&amp;gt; _events = [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Process managers have their own explicit identity. They are very similar&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// to aggregates or entities.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; OrderId _id;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// They can include other properties to track state as needed.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Process managers usually include an initialization procedure that kicks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// off the process, and provides the necessary parameters.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Initialize(&lt;span style=&#34;color:#888&#34;&gt;/* ... */&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(orderProcessingInitiated);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; CommandIssuedEvent(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            target: Target.PaymentService,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; ProcessPaymentCommand(@event.OrderId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// The event handlers are very similar to those of the event sourced&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// stateful saga. The difference is that here they run more complex logic to&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// determine how to continue the process. As opposed to the saga&amp;#39;s simpler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// mapping of incoming events to commands.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Process(PaymentAccepted @event)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(@event);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; CommandIssuedEvent(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            target: Target.InventoryService,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; PreparePackageCommand(@event.OrderId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Process(InventoryCleared @event)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(@event);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; CommandIssuedEvent(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            target: Target.ShippingService,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            command: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; ScheduleDeliveryCommand(@event.OrderId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Process(ShipmentDispatched @event)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Append(@event);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;architectural-patterns-to-use-within-bounded-contexts&#34;&gt;Architectural patterns to use within bounded contexts&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/architecture-concept-map.png&#34; alt=&#34;Concept map of the architectural patterns&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;These are the main concepts around the architectural patterns that we&amp;rsquo;ve discussed.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&#34;patterns-for-communicating-across-bounded-contexts&#34;&gt;Patterns for communicating across bounded contexts&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/communication-concept-map.png&#34; alt=&#34;Concept map of the communication patterns&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;These are the main concepts around the communication patterns that we&amp;rsquo;ve discussed.&lt;/em&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Implementing business logic with Domain-Driven Design</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2026/04/implementing-business-logic-ddd-part-2/"/>
      <id>https://www.endpointdev.com/blog/2026/04/implementing-business-logic-ddd-part-2/</id>
      <published>2026-04-21T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2026/04/implementing-business-logic-ddd-part-2/cover.webp&#34; alt=&#34;A panoramic view from a mountain overlooks a vast valley stretching to the horizon under a stormy sky.&#34;&gt;&lt;br&gt;
Photo by Bimal Gharti Magar, 2026.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is part 2 of a series of blog posts on Domain-Driven Design:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/&#34;&gt;High level system analysis and design with Domain-Driven Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/04/implementing-business-logic-ddd-part-2/&#34;&gt;Implementing business logic with Domain-Driven Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/&#34;&gt;Designing software architecture with Domain-Driven Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/05/applying-it-in-practice-ddd-part-4/&#34;&gt;Applying Domain-Driven Design in Practice&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Domain-Driven Design&lt;/strong&gt; is an approach to software development that focuses on, &lt;a href=&#34;https://www.oreilly.com/library/view/domain-driven-design-tackling/0321125215/&#34;&gt;as Eric Evans puts it&lt;/a&gt;, &amp;ldquo;tackling the complexity in the heart of software&amp;rdquo;. And what is in the heart of software? The business domain in which it operates. Or more specifically: a &lt;strong&gt;model&lt;/strong&gt; of it, made of code. That is, the code that implements the business logic that comes into play when solving problems within the realm of a particular business activity.&lt;/p&gt;
&lt;p&gt;DDD is not just about writing code though. It&amp;rsquo;s a whole methodology that touches on business needs, requirements gathering, organizational dynamics, high level architectural design, and lower level patterns for implementing software intensive systems.&lt;/p&gt;
&lt;p&gt;As a result, DDD offers a treasure trove of concepts, patterns and tools that can be applied to any software project, regardless of the size and complexity.&lt;/p&gt;
&lt;p&gt;In this series of blog posts we&amp;rsquo;re going to explore many aspects of DDD. We will be following the structure laid out by &lt;a href=&#34;https://vladikk.com/&#34;&gt;Vlad Khononov&lt;/a&gt;&amp;rsquo;s excellent book on the topic &amp;ldquo;&lt;a href=&#34;https://www.oreilly.com/library/view/learning-domain-driven-design/9781098100124/&#34;&gt;Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy&lt;/a&gt;&amp;rdquo;. So you can think of this series as a summary of that book. An abridged version that can serve as a review for anybody who has read it; but also as an entry point for people who are new to DDD.&lt;/p&gt;
&lt;h3 id=&#34;table-of-contents&#34;&gt;Table of contents&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#implementing-business-logic-with-domain-driven-design&#34;&gt;Implementing business logic with Domain-Driven Design&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#table-of-contents&#34;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-5-implementing-simple-business-logic&#34;&gt;Section 5: Implementing simple business logic&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#transaction-script&#34;&gt;Transaction script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#active-record&#34;&gt;Active record&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#when-to-use-them&#34;&gt;When to use them&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-6-tackling-complex-business-logic&#34;&gt;Section 6: Tackling complex business logic&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#domain-model&#34;&gt;Domain model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#value-object&#34;&gt;Value object&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#entities&#34;&gt;Entities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#aggregates&#34;&gt;Aggregates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#domain-event&#34;&gt;Domain event&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#domain-service&#34;&gt;Domain service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#data-access-concerns&#34;&gt;Data access concerns&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-7-modeling-the-dimension-of-time&#34;&gt;Section 7: Modeling the dimension of time&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#data-storage-and-retrieval&#34;&gt;Data storage and retrieval&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#advantages-and-disadvantages&#34;&gt;Advantages and disadvantages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#the-event-sourced-domain-model&#34;&gt;The event sourced domain model&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#ddd-tools-for-implementing-business-logic&#34;&gt;DDD tools for implementing business logic&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;section-5-implementing-simple-business-logic&#34;&gt;Section 5: Implementing simple business logic&lt;/h3&gt;
&lt;p&gt;Now that we&amp;rsquo;ve explored DDD&amp;rsquo;s higher level system design concepts, it&amp;rsquo;s time to zoom in and start looking at how to implement business logic: the most important part of software. We will start by discussing two patterns that are ideal for implementing simple business logic: transaction script and active record.&lt;/p&gt;
&lt;h4 id=&#34;transaction-script&#34;&gt;Transaction script&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://martinfowler.com/eaaCatalog/transactionScript.html&#34;&gt;According to Martin Fowler&lt;/a&gt;, the &lt;strong&gt;transaction script&lt;/strong&gt; pattern &amp;ldquo;organizes business logic by procedures where each procedure handles a single request from the presentation.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;For most programmers that have experience with procedural languages, the transaction script is easy to grasp. This pattern is about conceptualizing a system as a collection of transactions. And organizing these transactions as independent, transactional procedural scripts. Think one transaction script per use case, exposed for the users to invoke when they need to.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/implementing-business-logic-ddd-part-2/transaction-script.png&#34; alt=&#34;Transaction script example&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The core idea of transaction script is about putting business transactions front and center. Closely related scripts can be implemented as methods in a service class. Or you could also have a separate &amp;ldquo;service object&amp;rdquo; class for each script.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Indeed, on its own, the transaction script pattern is a simple way of organizing relatively simple domain logic. However, as we&amp;rsquo;ll see throughout the next few sections, it is also a foundational pattern that is present when implementing the more advanced ones. Also, even though the concept is simple, implementation of transaction script in the real world has to be done carefully, in order to avoid falling into common pitfalls, often related to ensuring atomicity.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;AddItemToQuote&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// This method adds a new item to a shopping cart and records the operation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// into a log table. Both operations are done separately.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Run(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quoteId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; productId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _db.ExecuteSql(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;$&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            INSERT INTO quote_items(quote_id, product_id, quantity)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            VALUES ({quoteId}, {productId}, {quantity});
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// If some error happens after the previous command and before the one&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// below, the system will be left in an inconsistent state. The new item&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// would have been added to the shopping cart, but the audit log would&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// not have a corresponding event.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _db.ExecuteSql(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;$&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            INSERT INTO quote_audit_log(
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;                quote_id, event_description, date_created
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            VALUES ({quoteId}, &amp;#39;Item added to quote&amp;#39;, CURRENT_DATE);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That is, a transaction script needs to actually be &lt;strong&gt;transactional&lt;/strong&gt;. It needs to be atomic. Of course, each domain&amp;rsquo;s requirements will dictate how true this is, but in general, we want transaction scripts to operate as a unit, and make sure that they don&amp;rsquo;t leave the system in an inconsistent state in situations when the script fails midway through its execution.&lt;/p&gt;
&lt;p&gt;If all the script does is interact with a relational database, it is easy to address this issue. The solution is to perform the operations within a &lt;strong&gt;database transaction&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;AddItemToQuoteWithTransaction&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Run(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quoteId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; productId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888&#34;&gt;// We can use a database transaction to fix the issue.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _db.BeginTransaction();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _db.ExecuteSql(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;$&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;                INSERT INTO quote_items(quote_id, product_id, quantity)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;                VALUES ({quoteId}, {productId}, {quantity});
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _db.ExecuteSql(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;$&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;                INSERT INTO quote_audit_log(
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;                    quote_id, event_description, date_created
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;                )
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;                VALUES ({quoteId}, &amp;#39;Item added to quote&amp;#39;, CURRENT_DATE);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _db.CommitTransaction();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;catch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _db.RollbackTransaction();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;throw&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The problem gets more complicated when the script performs a &lt;strong&gt;distributed transaction&lt;/strong&gt;. That is, when it interacts with other systems which are outside of the scope of a relational database transaction. For example, interacting with the file system, or with an external web service.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;AddItemToQuoteWithDistributedTransaction&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Run(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quoteId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; productId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _db.ExecuteSql(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;$&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            INSERT INTO quote_items(quote_id, product_id, quantity)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            VALUES ({quoteId}, {productId}, {quantity});
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// We&amp;#39;re back to our initial problem. Any failure at this point in the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// script will have updated the shopping cart without having sent the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// notification to the external system.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _api.NotifyQuoteItemAdded(quoteId, productId, quantity);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These are hard to deal with and often require very particular code to handle properly. Further in this series we&amp;rsquo;ll discuss this topic again when we talk about &lt;a href=&#34;https://martinfowler.com/bliki/CQRS.html&#34;&gt;CQRS&lt;/a&gt; and the &lt;a href=&#34;https://microservices.io/patterns/data/transactional-outbox.html&#34;&gt;outbox pattern&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Sometimes though, the distributed transaction is not as obvious. Consider a web application that exposes an endpoint to increment the quantity of particular shopping cart items by one:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;IncrementQuoteItemQuantity&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Run(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quoteItemId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _db.ExecuteSql(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;$&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            UPDATE quote_items
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            SET quantity = quantity + 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            WHERE id = {quoteItemId};
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even though that&amp;rsquo;s a single database operation, there is inter-system communication between the user&amp;rsquo;s browser and the application server; and between the application server and the database. A network outage, for example, that happens in the middle of this operation can leave the system in an inconsistent state. If it fails after it&amp;rsquo;s already submitted the &lt;code&gt;UPDATE&lt;/code&gt; command to the database, and responds with a failure message to the client, the client might attempt to try and increase the quantity again. This would result in the increment being done two times, when actually it should have been only one.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/implementing-business-logic-ddd-part-2/implicit-distributed-transaction.png&#34; alt=&#34;Implicit distributed transaction&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;In the context of client-server applications, some transactions are distributed even though at first glance they might not seem that way. In this simple operation, there are three systems interacting over the network: The client, the application server and the database.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are two possible ways of handling this type of situation: making the operation idempotent, or using optimistic concurrency control.&lt;/p&gt;
&lt;p&gt;One way to make the operation &lt;strong&gt;idempotent&lt;/strong&gt;, that is, to make sure that it always produces the same results, no matter how many times it&amp;rsquo;s done, is to have the caller specify the quantity to update the item to. That is, changing the operation from &amp;ldquo;add one quantity&amp;rdquo;, to &amp;ldquo;set the quantity to this&amp;rdquo;. Like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;IncrementQuoteItemQuantityIdempotent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Run(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quoteItemId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _db.ExecuteSql(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;$&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            UPDATE quote_items
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            SET quantity = {quantity}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            WHERE id = {quoteItemId};
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On the other hand, implementing &lt;strong&gt;optimistic concurrency control&lt;/strong&gt; in this scenario could be done by effectuating the record update while using the current state of the record when checking for a match. This boils down to including the expected values of the different fields in the &lt;code&gt;WHERE&lt;/code&gt; clause. This way, the record will be updated only if it is in the state that the caller expected it to be:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;IncrementQuoteItemQuantityOptimisticConcurrency&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Run(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quoteItemId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _db.ExecuteSql(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;$&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            UPDATE quote_items
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            SET quantity = quantity + 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            WHERE id = {quoteItemId} AND quantity = {quantity};
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In both cases, the caller would have to obtain the current state of the record before attempting the update operation.&lt;/p&gt;
&lt;h4 id=&#34;active-record&#34;&gt;Active record&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://www.martinfowler.com/eaaCatalog/activeRecord.html&#34;&gt;Martin Fowler describes&lt;/a&gt; the &lt;strong&gt;active record&lt;/strong&gt; pattern as &amp;ldquo;an object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The main advantage of the active record pattern is that it greatly simplifies database access. Especially when leveraging &lt;a href=&#34;https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping&#34;&gt;ORM frameworks&lt;/a&gt;. With active record, you usually end up with a set of classes that closely mirror your database structure. You have one class per table, where the fields of those classes represent table columns and the instances of those classes represent individual records in those tables.&lt;/p&gt;
&lt;p&gt;So, when the underlying data model is complex, business logic organized in transaction scripts can be augmented with active record to reduce a great deal of the complexity involved in database interactions. This is because the active record pattern takes care of mapping between database records and in-memory objects, as well as all the data retrieval and manipulation commands (I.e. the &lt;a href=&#34;https://en.wikipedia.org/wiki/Create,_read,_update_and_delete&#34;&gt;CRUD&lt;/a&gt;). This allows the transaction scripts to focus more on domain logic, and less on manipulating the underlying data.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;AddItemToQuoteActiveRecord&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Run(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quoteId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; productId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// Active record allows us to work with objects instead of directly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// issuing database commands.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; item = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; QuoteItem
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            QuoteId = quoteId,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ProductId = productId,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            Quantity = quantity
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item.Save();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Of course, active records can (and should) also include domain logic. Aspects like the relationships between the domain entities, validation, complex database queries, and even the business procedures that involve them are all part of an active record object.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# This class is an Order active record. It establishes relationships with other&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# entities, defines basic validation rules, and has custom queries.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Order&lt;/span&gt; &amp;lt;  &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ApplicationRecord&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.table_name = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;orders&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    enum &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:status&lt;/span&gt;, [ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:pending&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:paid&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:shipped&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:cancelled&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    enum &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:payment_method&lt;/span&gt;, [ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:cash&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:credit_card&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:transfer&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    has_many &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:order_items&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    has_one &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:shipping_address&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    has_one &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:billing_address&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    validates &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:email&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;presence&lt;/span&gt;: &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;email&lt;/span&gt;: {&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;mode&lt;/span&gt;: &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:strict&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    validates &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:phone&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;length&lt;/span&gt;: {&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;minimum&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;allow_blank&lt;/span&gt;: &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scope &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sorted&lt;/span&gt;, -&amp;gt; { order(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;created_at&lt;/span&gt;: &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:desc&lt;/span&gt;) }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scope &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:new_ones&lt;/span&gt;, -&amp;gt; { where(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;created_at &amp;gt;= ?&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;.day.ago) }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scope &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:have_phone&lt;/span&gt;, -&amp;gt; { where.not(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;phone&lt;/span&gt;: &lt;span style=&#34;color:#080&#34;&gt;nil&lt;/span&gt;) }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scope &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:where_email_is&lt;/span&gt;, -&amp;gt;(email) { where(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;lower(email) = ?&amp;#34;&lt;/span&gt;, email.downcase) }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# The framework augments it with data access logic, like querying:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;order = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Order&lt;/span&gt;.find(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;expensive_items = order.order_items.where(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;price &amp;gt;= ?&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;200&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;orders_from_us = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Order&lt;/span&gt;.where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;country_code&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;US&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_orders = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Order&lt;/span&gt;.new_ones.sorted
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# ...and making changes:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_order = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Order&lt;/span&gt;.new(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;email&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;test@email.com&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_order.save
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;order.update(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;shipping_number&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1234567890&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;status&lt;/span&gt;: &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:shipped&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;The &lt;a href=&#34;https://guides.rubyonrails.org/active_record_basics.html&#34;&gt;Active Record&lt;/a&gt; framework from Ruby on Rails is a great implementation of the pattern that puts business logic in a centralized location while &lt;em&gt;magically&lt;/em&gt; extending the objects with all the data access logic they need.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is a big step up from raw transaction scripts, but still it has its own disadvantages. First, the active records can end up having too much responsibility, and grow to unmanageable sizes, especially for core business entities. Second, the active record pattern by itself does not enforce access control on its properties. Meaning that external processes can freely modify their state, potentially ignoring any business rules that could bind them.&lt;/p&gt;
&lt;p&gt;Something else to be aware of is that when active records define little to no behavior, that is, when they don&amp;rsquo;t implement business logic, they become an &lt;a href=&#34;https://martinfowler.com/bliki/AnemicDomainModel.html&#34;&gt;anemic domain model&lt;/a&gt;. Active record objects that focus only on database access tasks, and higher level &amp;ldquo;service objects&amp;rdquo; that manipulate them and implement all the domain logic themselves are the hallmark of anemic domain models.&lt;/p&gt;
&lt;p&gt;An anemic domain model may look like a full fledged domain model, with all the objects representing domain concepts and their relationships, but they are hollow. Their most important part is missing. In the next section we will see what a real domain model looks like, DDD&amp;rsquo;s definitive pattern for implementing business logic in complex subdomains.&lt;/p&gt;
&lt;h4 id=&#34;when-to-use-them&#34;&gt;When to use them&lt;/h4&gt;
&lt;p&gt;In some circles, the transaction script and active record patterns are considered anti patterns. But really, they are just tools for the job. When misapplied, they become detrimental, but when used to solve the problems they are good at, they shine. In fact, they can give you a lot of bang for your design buck. But when the domain logic you&amp;rsquo;re implementing is very complex, they can begin to fall short, as their relatively low level of abstraction becomes insufficient and prone to code repetition, and inconsistencies when this repeated code goes out of sync. Which is a big problem when dealing with complex subdomains.&lt;/p&gt;
&lt;h3 id=&#34;section-6-tackling-complex-business-logic&#34;&gt;Section 6: Tackling complex business logic&lt;/h3&gt;
&lt;p&gt;When implementing complex business logic, the patterns that we&amp;rsquo;ve seen up to this point can only get you so far. They start to leave a lot to be desired due to their relatively low level of abstraction. When the situation calls for a higher level of abstraction, in order to produce a more supple design, DDD calls for the domain model pattern.&lt;/p&gt;
&lt;h4 id=&#34;domain-model&#34;&gt;Domain model&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://martinfowler.com/eaaCatalog/domainModel.html&#34;&gt;Taking from Martin Fowler&amp;rsquo;s definition&lt;/a&gt;, we learn that the &lt;strong&gt;domain model&lt;/strong&gt; is a full object-oriented model of the domain, that incorporates both behavior and data. Domain models take the form of a large web of interconnected objects, where each one represents a meaningful concept in the business domain.&lt;/p&gt;
&lt;p&gt;In his book, Eric Evans expanded greatly on this definition, introducing a set of patterns and tools for implementing domain models. Indeed, to build a domain model, we incorporate other building block patterns: value objects, entities, aggregates, domain events, domain services.&lt;/p&gt;
&lt;p&gt;Also, there are two main rules that a domain model needs to follow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There should be no dependency on particular frameworks or infrastructure. Objects in the domain model should be &lt;strong&gt;plain old objects&lt;/strong&gt;, focused only on domain logic.&lt;/li&gt;
&lt;li&gt;The domain model should speak the &lt;strong&gt;ubiquitous language&lt;/strong&gt; of the &lt;strong&gt;bounded context&lt;/strong&gt; in which it operates. That means that all identifiers should call back to business concepts and it should represent the mental model of the domain experts.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;value-object&#34;&gt;Value object&lt;/h4&gt;
&lt;p&gt;A &lt;strong&gt;value object&lt;/strong&gt; is an object whose identity is given by its properties. That is, its value. It does not have an explicit identifier, like an Id field. Consider for example a &lt;code&gt;Point&lt;/code&gt; object:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Point&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;double&lt;/span&gt; _x;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;double&lt;/span&gt; _y;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The X and Y values completely define a point. Two instances of this class with the same X and Y coordinates represent the same point. Changing the value of either coordinate produces a new point.&lt;/p&gt;
&lt;p&gt;They are useful for representing properties of other objects and are usually implemented as &lt;strong&gt;immutable objects&lt;/strong&gt;. One of the great strengths of value objects is that they allow the model to speak the ubiquitous language by replacing primitives with bespoke small objects that make the code clearer and encapsulate related business logic.&lt;/p&gt;
&lt;p&gt;As an example, consider this &lt;code&gt;Order&lt;/code&gt; class.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; _id;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;string&lt;/span&gt; _status;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;string&lt;/span&gt; _email;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;string&lt;/span&gt; _phone;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;double&lt;/span&gt; _shippingWeight;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;string&lt;/span&gt; _countryCode;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; Order(&lt;span style=&#34;color:#888&#34;&gt;/*...*/&lt;/span&gt;) {&lt;span style=&#34;color:#888&#34;&gt;/*...*/&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// This class can be instantiated like this:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; order = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Order(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;12345&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    status: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Processing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;test@email.com&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    phone: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;123-456-7890&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    shippingWeight: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    countryCode: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;US&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, all the values are assigned by convention. An email, a phone number, a status&amp;hellip; They are all just strings with no special behavior or meaning. We must know what they look like beforehand in order to assign them correctly.&lt;/p&gt;
&lt;p&gt;If we use value objects, this class could be implemented like this instead:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; OrderId _id;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; OrderStatus _status;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; PersonName _name;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; EmailAddress _email;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; PhoneNumber _phone;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; Weight _shippingWeight;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; CountryCode _country;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; Order(&lt;span style=&#34;color:#888&#34;&gt;/*...*/&lt;/span&gt;) {&lt;span style=&#34;color:#888&#34;&gt;/*...*/&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Now, instances can be created like this:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; order = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Order(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; OrderId(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;12345&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    status: OrderStatus.Processing,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; PersonName(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Kevin&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Campusano&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email: EmailAddress.Parse(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;test@email.com&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    phone: PhoneNumber.Parse(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1234567890&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    shippingWeight: Weight.FromLbs(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10.25&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    country: CountryCode.Parse(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;US&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This approach has many advantages:&lt;/p&gt;
&lt;p&gt;First of all the &lt;code&gt;Order&lt;/code&gt; class does not have to validate its fields. Validation can happen in the value objects. This is good because it allows other domain objects to have fields of the same type without having to duplicate the validation logic themselves. Imagine you also have a contact object somewhere in your model that includes a phone number field, for example. Both it and &lt;code&gt;Order&lt;/code&gt; can reuse the phone number value object, and the logic it carries.&lt;/p&gt;
&lt;p&gt;Secondly, value objects can capture the business logic that&amp;rsquo;s closely related to them. A phone number field, for example can implement methods to obtain further information about it like its area code or the country it belongs to. A &amp;ldquo;weight&amp;rdquo; value object can implement logic for converting from one measuring system to another.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Here&amp;#39;s a weight value object that encapsulates logic like converting from one&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// unit to another and comparing the magnitude of different values.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Weight&lt;/span&gt; : IComparable&amp;lt;Weight&amp;gt;, IEquatable&amp;lt;Weight&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;double&lt;/span&gt; LbsPerKg = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2.20462&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;double&lt;/span&gt; _lbs;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; Weight(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;double&lt;/span&gt; lbs) { _lbs = lbs; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;static&lt;/span&gt; Weight FromLbs(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;double&lt;/span&gt; lbs) =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Weight(lbs);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;static&lt;/span&gt; Weight FromKgs(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;double&lt;/span&gt; kgs) =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Weight(kgs * LbsPerKg);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;double&lt;/span&gt; ToLbs() =&amp;gt; _lbs;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;double&lt;/span&gt; ToKgs() =&amp;gt; _lbs / LbsPerKg;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; CompareTo(Weight? other) =&amp;gt; ToLbs().CompareTo(other?.ToLbs());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;bool&lt;/span&gt; Equals(Weight? other) =&amp;gt; ToLbs().Equals(other?.ToLbs());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, using value objects lets the model speak the ubiquitous language, and thus makes it clearer. Strongly typing properties in this way, beyond just using language primitives, very clearly captures the intent of the property.&lt;/p&gt;
&lt;h4 id=&#34;entities&#34;&gt;Entities&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Entities&lt;/strong&gt; are objects that represent the concepts in the domain that have a lifecycle and explicit identification. A person, an order, a lead, a transaction. These are all examples of entities. Entities, as opposed to value objects, are &lt;strong&gt;not immutable&lt;/strong&gt;, and are expected to change throughout their life in the system. Value objects, like we saw before, are ideal for representing properties of entities.&lt;/p&gt;
&lt;p&gt;Entities are a core building block for a domain model. However, they are not used independently. They are used as part of an aggregate.&lt;/p&gt;
&lt;h4 id=&#34;aggregates&#34;&gt;Aggregates&lt;/h4&gt;
&lt;p&gt;An &lt;strong&gt;aggregate&lt;/strong&gt; is a &lt;strong&gt;hierarchy&lt;/strong&gt; of entities and value objects that are bound together by closely related business logic. The aggregate forms a boundary that protects the consistency of the objects that compose it. It achieves this by preventing external objects from directly modifying its state and defining a public interface through which other parts of the system can interact with it.&lt;/p&gt;
&lt;p&gt;The rest of the system cannot directly mutate the state of the entities within an aggregate. They can only do so via the methods exposed by the aggregate&amp;rsquo;s public interface. These methods, so called &lt;strong&gt;commands&lt;/strong&gt;, encapsulate the aggregate&amp;rsquo;s business logic and protect them from corruption, by enforcing the necessary validations and invariants. Indeed, all the business logic that&amp;rsquo;s closely related to the aggregate lives in one place: the aggregate itself.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Here we have an aggregate that represents a shopping cart and its items.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Quote&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// It does not expose its list of items publicly. Instead, it implements&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// public methods for manipulating them.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    IList&amp;lt;QuoteItem&amp;gt; _items = [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    QuoteItem? GetItemBy(ProductId productId) =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _items.FirstOrDefault(i =&amp;gt; i.ProductId == productId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; AddItem(ProductId productId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; item = GetItemBy(productId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (item != &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; InvalidOperationException(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Item already exists.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _items.Add(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; QuoteItem
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ProductId = productId,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            Quantity = quantity
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Even though this method modifies an item, and not the quote itself, it&amp;#39;s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// still implemented here, because this is the aggregate root.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; SetItemQuantity(ProductId productId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; item = GetItemBy(productId) ??
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; InvalidOperationException(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Item does not exist.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item.Quantity = quantity;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These commands, which are the public interface of an aggregate, should all be defined in a single entity within the aggregate. We call this entity the &lt;strong&gt;aggregate root&lt;/strong&gt;. If the aggregate is a hierarchy of objects, and we can picture it as a tree, then the root is the object that exists at the root of the tree, where all branches come from.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/implementing-business-logic-ddd-part-2/aggregate-root.png&#34; alt=&#34;The aggregate root&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The aggregate is a hierarchy of objects. The aggregate root is the sole object in this hierarchy with which other components interact.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This focus on commands makes the implementation of the application layer components more straightforward. I.e. they become &lt;strong&gt;transaction scripts&lt;/strong&gt;. By &amp;ldquo;application layer components&amp;rdquo;, I mean those components that orchestrate calls to the domain model in order to fulfill use cases in response to, say, user requests. They follow a general pattern of:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Load the aggregate. Typically from persistent storage.&lt;/li&gt;
&lt;li&gt;Invoke the desired command.&lt;/li&gt;
&lt;li&gt;Persist the new state of the aggregate.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Here&amp;#39;s an application service leveraging the quote aggregate to add an item&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// to a shopping cart.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;AddItemToQuote&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; Result Run(QuoteId quoteId, ProductId productId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; quote = _quoteRepository.FindById(quoteId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (quote == &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Result.Error(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Quote not found.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            quote.AddItem(productId, quantity);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _quoteRepository.Save(quote);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Result.Success();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// Notice how the application layer logic takes care of database related&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// errors. Remember that the aggregate is a plain old object which has&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// no knowledge about infrastructure or frameworks. The data access&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// logic, in this case encapsulated in the _quoteRepository, is the one&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// that will produce such errors when trying to commit changes to the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// underlying storage.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;catch&lt;/span&gt; (ConcurrencyException ex)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Result.Error(ex);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are other rules that aggregates need to follow. One of them is that the aggregate acts as a &lt;strong&gt;transaction boundary&lt;/strong&gt; for aggregate operations. That is, all changes to an aggregate should be transactional, atomic. Also, no system operation should involve a transaction that includes different aggregates. We should have one aggregate per database transaction.&lt;/p&gt;
&lt;p&gt;This reveals another aspect of aggregates: An aggregate should expect &lt;strong&gt;strong consistency&lt;/strong&gt; only on its own objects. For objects that are outside of the aggregate, eventual consistency should suffice. Or, looking at it from a different angle, this means that when designing an aggregate, data consistency is a guiding principle. The data that needs to be strongly consistent in order to fulfill the business requirements, should be included in the aggregate. The data that can be eventually consistent and still meet the requirements, probably belongs in a different aggregate.&lt;/p&gt;
&lt;p&gt;At first glance, all these rules for aggregates may seem overly limiting. But the main idea is to keep their scope as constrained as possible, to prevent them from growing too much and taking on too many responsibilities. We should strive to keep aggregates small, highly cohesive, and decoupled from other system components. That unlocks the ability for them to be reorganized and reused in many ways. This helps avoid code duplication when fulfilling the requirements of today while also reducing the cost of evolving to meet the requirements of tomorrow.&lt;/p&gt;
&lt;h4 id=&#34;domain-event&#34;&gt;Domain event&lt;/h4&gt;
&lt;p&gt;Through their commands, the outside world can send messages to aggregates. &lt;strong&gt;Domain events&lt;/strong&gt; are the mechanism through which aggregates can themselves send messages to the outside world. As their name suggests, domain events are messages that describe important events that have happened in the business domain, related to an aggregate. Think &amp;ldquo;order placed&amp;rdquo;, &amp;ldquo;user registered&amp;rdquo; or &amp;ldquo;product out of stock&amp;rdquo;. The events should provide all necessary data that allows consumers to understand what has happened.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// This JSON data describes the event of an item being added to a shopping cart.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// It includes all the details that subscribers need to know about the event.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;quote-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;f3774200-9e57-4ad2-9d93-ffb9e92b8364&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event-type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;item-added-to-quote&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event-time&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1628970815&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;product-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;7e7aee52-9aa1-4e0e-810e-666cedab5a7a&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Quote&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    IList&amp;lt;DomainEvent&amp;gt; _domainEvents = [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; AddItem(ProductId productId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// This command creates the event and adds it to the aggregate&amp;#39;s list.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _domainEvents.Add(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; ItemAddedToQuote(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            quoteId: _id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            productId: productId,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            quantity: quantity
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Domain events are also part of an aggregate&amp;rsquo;s public interface. Just like its commands. Other parts of the system can subscribe to these events, and when they happen, react accordingly. We will learn more about domain events, and see how to push them to subscribers, later in the series.&lt;/p&gt;
&lt;h4 id=&#34;domain-service&#34;&gt;Domain service&lt;/h4&gt;
&lt;p&gt;Sometimes there&amp;rsquo;s business logic that doesn&amp;rsquo;t belong to a particular aggregate or value object, or that involves multiple aggregates. &lt;strong&gt;Domain services&lt;/strong&gt; can be implemented in these cases. Domain services are simple stateless objects that implement some business logic. Somewhat like an aggregate&amp;rsquo;s command, but defined outside of an aggregate.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Domain services are a good solution for implementing logic that orchestrates&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// calls to different system components.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;PlaceOrder&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; Result Run(QuoteId quoteId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; quote = _quoteRepository.FindById(quoteId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (quote == &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Result.Error(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Quote not found.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            quote.Close();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _quoteRepository.Save(quote);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; order = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Order(quote);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            order.PlaceOrder();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _orderRepository.Save(order);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888&#34;&gt;// We can imagine the above code producing an &amp;#34;order placed&amp;#34; event,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888&#34;&gt;// and a separate payment processing component picking it up and&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888&#34;&gt;// getting to work. Then, inventory and shipping components could&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888&#34;&gt;// continue processing the order after the payment is successful.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Result.Success();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;catch&lt;/span&gt; (ConcurrencyException ex)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Result.Error(ex);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Of course, the rule of &amp;ldquo;modify only one aggregate per transaction&amp;rdquo; still applies. Even for domain services that orchestrate business operations that involve multiple aggregates. Remember, if strong consistency is needed across separate aggregates, and thus they have operations that need to be executed within the same transaction, then maybe these objects should be part of the same aggregate in the first place.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also important to consider domain services as a sort of last resort. A final domain modeling tool to use only when the other tools like aggregates, value objects, commands and domain events, fall short and truly can&amp;rsquo;t meet the requirements on their own.&lt;/p&gt;
&lt;h4 id=&#34;data-access-concerns&#34;&gt;Data access concerns&lt;/h4&gt;
&lt;p&gt;As we&amp;rsquo;ve stated at the beginning, the domain model should be made of plain old objects. That means no dependency on framework components or particular infrastructure, just pure domain logic. Data access logic is one of those things that it should be oblivious about.&lt;/p&gt;
&lt;p&gt;In practical terms, your choice of technology stack and software development framework will usually dictate the mechanisms you use for interacting with persistent data storage. But that doesn&amp;rsquo;t matter to the domain model, with the correct abstraction, it should be compatible with any data access mechanism. Here are a few patterns worth mentioning:&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve already seen &lt;a href=&#34;https://www.martinfowler.com/eaaCatalog/activeRecord.html&#34;&gt;active record&lt;/a&gt; being used as a data access strategy. Particularly useful when paired with an ORM framework. Due to its nature of tieing infrastructure concerns (i.e. data access logic) with the business logic, you have to jump through some hoops to make it work with a domain model. But it can be done. The &lt;a href=&#34;https://martinfowler.com/eaaCatalog/repository.html&#34;&gt;repository pattern&lt;/a&gt; is also a good fit for solving the data access problem in the context of DDD. It offers a clear, intention-revealing interface for data retrieval and modification. &lt;a href=&#34;https://martinfowler.com/eaaCatalog/unitOfWork.html&#34;&gt;Unit of work&lt;/a&gt; is also a pattern worth looking into, for coordinating numerous database operations.&lt;/p&gt;
&lt;p&gt;But the main takeaway is this: the domain model does not concern itself with data access, or frameworks, or infrastructure. So make sure to keep it plain and use abstractions to keep it unconcerned.&lt;/p&gt;
&lt;h3 id=&#34;section-7-modeling-the-dimension-of-time&#34;&gt;Section 7: Modeling the dimension of time&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;event sourced domain model&lt;/strong&gt; is a further evolution of the domain model which incorporates the dimension of &lt;strong&gt;time&lt;/strong&gt;. By leveraging domain events as the source of truth for system data, it allows for a model that can provide deeper insight into the data, rich audit logging, and visibility into the state of the aggregates and entities at any previous point in their lifecycle.&lt;/p&gt;
&lt;h4 id=&#34;data-storage-and-retrieval&#34;&gt;Data storage and retrieval&lt;/h4&gt;
&lt;p&gt;The main characteristic that differentiates event sourcing from a traditional domain modeling is how the data that represents the aggregates is persisted. Instead of persisting the aggregate&amp;rsquo;s current state, event sourced domain models persist the domain events produced by the aggregates. These domain events are generated as a result of any operation that changes the state of the aggregate. Then, to obtain the current state of the aggregates, all the events are retrieved from storage and used to reconstruct the full object in memory.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/implementing-business-logic-ddd-part-2/event-sourced-aggregate.png&#34; alt=&#34;The event sourcing data flow&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;In an event sourced domain model, aggregates produce events and commit them to the event store. To instantiate aggregates then, the same events are fetched from the event store and used to rehydrate the in-memory objects.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For example, in a database that backs an order processing system, an &lt;code&gt;orders&lt;/code&gt; table might look like this:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;id&lt;/th&gt;
          &lt;th&gt;status&lt;/th&gt;
          &lt;th&gt;email&lt;/th&gt;
          &lt;th&gt;phone&lt;/th&gt;
          &lt;th&gt;shipping_weight&lt;/th&gt;
          &lt;th&gt;country_code&lt;/th&gt;
          &lt;th&gt;created_at&lt;/th&gt;
          &lt;th&gt;updated_at&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;pending&lt;/td&gt;
          &lt;td&gt;john.doe@example.com&lt;/td&gt;
          &lt;td&gt;555-0101&lt;/td&gt;
          &lt;td&gt;2.5&lt;/td&gt;
          &lt;td&gt;US&lt;/td&gt;
          &lt;td&gt;2025-01-10&lt;/td&gt;
          &lt;td&gt;2025-01-10&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;2&lt;/td&gt;
          &lt;td&gt;paid&lt;/td&gt;
          &lt;td&gt;sarah.smith@example.com&lt;/td&gt;
          &lt;td&gt;555-0102&lt;/td&gt;
          &lt;td&gt;5.3&lt;/td&gt;
          &lt;td&gt;CA&lt;/td&gt;
          &lt;td&gt;2025-01-09&lt;/td&gt;
          &lt;td&gt;2025-01-11&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;3&lt;/td&gt;
          &lt;td&gt;shipped&lt;/td&gt;
          &lt;td&gt;mike.johnson@example.com&lt;/td&gt;
          &lt;td&gt;555-0103&lt;/td&gt;
          &lt;td&gt;1.8&lt;/td&gt;
          &lt;td&gt;GB&lt;/td&gt;
          &lt;td&gt;2025-01-08&lt;/td&gt;
          &lt;td&gt;2025-01-12&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;4&lt;/td&gt;
          &lt;td&gt;pending&lt;/td&gt;
          &lt;td&gt;emma.wilson@example.com&lt;/td&gt;
          &lt;td&gt;555-0104&lt;/td&gt;
          &lt;td&gt;3.2&lt;/td&gt;
          &lt;td&gt;AU&lt;/td&gt;
          &lt;td&gt;2025-01-11&lt;/td&gt;
          &lt;td&gt;2025-01-11&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;5&lt;/td&gt;
          &lt;td&gt;cancelled&lt;/td&gt;
          &lt;td&gt;david.brown@example.com&lt;/td&gt;
          &lt;td&gt;555-0105&lt;/td&gt;
          &lt;td&gt;4.7&lt;/td&gt;
          &lt;td&gt;US&lt;/td&gt;
          &lt;td&gt;2025-01-07&lt;/td&gt;
          &lt;td&gt;2025-01-13&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Here, each row represents an order and their current state. The database schema resembles the order domain entity.&lt;/p&gt;
&lt;p&gt;With event sourcing, what we persist into our database is a series of events that capture the full history of each order, as they journey through the system:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;order_created&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2025-01-10T10:00:00Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;john.doe@example.com&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;phone&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;555-0101&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;country_code&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;US&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;pending&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;payment_failed&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2025-01-11T12:00:00Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;payment_failed&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;reason&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;insufficient_funds&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;payment_details_updated&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2025-01-11T12:05:00Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;pending&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;payment_method_nonce&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;new-nonce-xyz&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;payment_succeeded&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2025-01-11T12:05:00Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;paid&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;inventory_stock_updated&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2025-01-11T15:00:00Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;processing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;items&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;product_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;A1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;product_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;B2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;shipping_scheduled&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2025-01-12T09:00:00Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;awaiting_shipment&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;shipping_number&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;TRACK1234567890&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;event_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;order_shipped&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2025-01-13T14:00:00Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;shipped&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We call this database, the &lt;strong&gt;event store&lt;/strong&gt;. This is an append-only storage mechanism that needs to support two features: fetching events that belong to a particular business entity and adding new ones.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// An event store only needs to support two features: fetching and appending.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;IEventStore&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    IEnumerable&amp;lt;DomainEvent&amp;gt; Fetch(Guid Id);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Append(Guid Id, IEnumerable&amp;lt;DomainEvent&amp;gt; events, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; expectedVersion);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In code, when we want to retrieve an order from storage, what we do is fetch all its events, iterate over them and apply the changes they represent to an in-memory object, until it is fully reconstructed, or &amp;ldquo;rehydrated&amp;rdquo;. In the context of event sourcing, we call these representations of collections of events, &lt;strong&gt;projections&lt;/strong&gt;. In an event sourced domain model, aggregates leverage these projections to figure out their current state.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// This is a hypothetical order aggregate in an event sourced domain model.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Its constructor expects a collection of domain events which it iterates over&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// to apply them to its internal state representaton object.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; List&amp;lt;DomainEvent&amp;gt; _events = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; OrderStateProjection _state = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; Order(IEnumerable&amp;lt;DomainEvent&amp;gt; events)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _state = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; OrderStateProjection();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; e &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;in&lt;/span&gt; events)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _events.Add(e);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _state.Apply((&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;dynamic&lt;/span&gt;)e);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Properties can be exposed to the outside world by leveraging the state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// projection.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; OrderId Id =&amp;gt; _state.Id;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; Version =&amp;gt; _state.Version;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// This is a general use event sourced projection of an order, used to capture&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// the current state of an order entity or aggregate.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;OrderStateProjection&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// This projection is meant to expose the full state of the order aggregate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// so it includes all the properties that belong to the order.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; OrderId Id { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// With event sourcing, we need to keep track of the version of the entity.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Every event that happens increments the version number.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; Version { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; OrderStatus Status { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; PersonName Name { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; EmailAddress Email { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; PhoneNumber Phone { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; Weight ShippingWeight { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; CountryCode Country { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;string&lt;/span&gt; PaymentMethodNonce { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;bool&lt;/span&gt; InventoryResolved { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;string&lt;/span&gt; TrackingNumber { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// It defines a series of &amp;#34;Apply&amp;#34; method overloads, one for each supported&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// domain event. The Order aggregate from above passes all the domain events&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// to the various overloads, calling them one by one, in order, until the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// full state is rehydrated into the projection. Each &amp;#34;Apply&amp;#34; overload takes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// the data captured in its event and assigns it to the correct property in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// the projection.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(OrderCreatedEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Id = e.Id;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Status = e.Status;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Name = e.Name;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Email = e.Email;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Phone = e.Phone;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ShippingWeight = e.ShippingWeight;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Country = e.Country;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(PaymentFailedEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Status = OrderStatus.PaymentFailed;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(PaymentDetailsUpdatedEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Status = OrderStatus.Pending;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        PaymentMethodNonce = e.PaymentMethodNonce;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(PaymentSucceededEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Status = OrderStatus.Processing;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(InventoryStockUpdatedEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Status = OrderStatus.Processing;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        InventoryResolved = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(ShippingScheduledEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Status = OrderStatus.AwaitingShipment;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        TrackingNumber = e.TrackingNumber;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(OrderShippedEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Status = OrderStatus.Shipped;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice the &lt;code&gt;Version&lt;/code&gt; field in the projection, which indicates the number of changes that the entity has gone through.&lt;/p&gt;
&lt;p&gt;On the other hand, when it comes to appending new events for an aggregate, the event store needs to be aware of potential concurrency problems and handle them. After all, the ordering of the events matter. That&amp;rsquo;s why the event store usually implements &lt;strong&gt;optimistic concurrency control&lt;/strong&gt;, leveraging the version field we touched on earlier. Essentially, when trying to append new events, the version being worked with is specified. If it doesn&amp;rsquo;t match the current version of the aggregate in the event store (maybe because some other process appended a new event), then the operation has to fail, or otherwise adapt to make sure the data remains consistent.&lt;/p&gt;
&lt;h4 id=&#34;advantages-and-disadvantages&#34;&gt;Advantages and disadvantages&lt;/h4&gt;
&lt;p&gt;One advantage of this design is that we can produce many distinct projections of the same underlying event sourced data. In the previous example, we saw a projection that represents the full current state of the order. But we might need other projections which are optimized for different use cases.&lt;/p&gt;
&lt;p&gt;For example here&amp;rsquo;s a projection that captures all the statuses that an order went through, which might help different business analysis use cases:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// This projection only cares about the changes in status of the order, so&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// instead of attempting to capture its full state, in keeps track of all the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// statuses in a collection.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;OrderStatusHistoryProjection&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; OrderId Id { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt; Version { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;get&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; List&amp;lt;OrderStatus&amp;gt; Statuses = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// All the &amp;#34;Apply&amp;#34; overloads capture the status change that each event&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// caused.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(OrderCreatedEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Statuses.Add(OrderStatus.Pending);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(PaymentFailedEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Statuses.Add(OrderStatus.PaymentFailed);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(PaymentDetailsUpdatedEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Statuses.Add(OrderStatus.Pending);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(PaymentSucceededEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Statuses.Add(OrderStatus.Processing);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(InventoryStockUpdatedEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Statuses.Add(OrderStatus.Processing);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(ShippingScheduledEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Statuses.Add(OrderStatus.AwaitingShipment);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Apply(OrderShippedEvent e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Statuses.Add(OrderStatus.Shipped);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Version++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can even store these projections into separate databases if we need to. We&amp;rsquo;ll learn more about that aspect later when we discuss CQRS.&lt;/p&gt;
&lt;p&gt;Another feature that we can implement easily thanks to event sourcing is time travel. We can easily apply the stored events up to a specific point in time or up to a specific version number and obtain the state of the entity as it was at that time. This can, for example, help troubleshooting, and unlock more avenues for business analysis.&lt;/p&gt;
&lt;p&gt;An obvious disadvantage of event sourcing is the complexity that it introduces. This complexity, compounded by the potential learning curve on teams that aren&amp;rsquo;t used to this kind of design, can be very detrimental when utilized in projects that don&amp;rsquo;t need it. As usual, follow DDD&amp;rsquo;s core principle of tying the system&amp;rsquo;s design to the domain&amp;rsquo;s needs, make sure to use the right tool for the job, and only deploy an event sourced domain model when the situation really calls for it. That is, whenever requirements dictate that the features enabled by such a design are necessary.&lt;/p&gt;
&lt;h4 id=&#34;the-event-sourced-domain-model&#34;&gt;The event sourced domain model&lt;/h4&gt;
&lt;p&gt;So, in summary, an event sourced domain model is a domain model that uses the event sourcing pattern to represent and operate on its aggregates. In a traditional, non-event-sourced domain model, the current state of the aggregates is persisted, commands modify this state and domain events are emitted for certain important operations, when needed. In the event sourced variant, domain events are used much more frequently, as they are the only source of truth. Everything and anything that changes the state of the aggregates produces an event. No changes are done directly, only through events. This is necessary because it is the events that are persisted, and it is from these events that the aggregates&amp;rsquo; current state is derived.&lt;/p&gt;
&lt;p&gt;In general, operations involving event sourced aggregates go through the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Load the aggregate&amp;rsquo;s domain events from the event store.&lt;/li&gt;
&lt;li&gt;Reconstruct the aggregate&amp;rsquo;s state using these events. You can use the particular projection needed for the task at hand.&lt;/li&gt;
&lt;li&gt;Run the necessary aggregate commands. Which in turn produce new domain events.&lt;/li&gt;
&lt;li&gt;Append the new events into the event store. Making sure to handle any concurrency errors.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;RescheduleOrderForPayment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; IOrderRepository _orderRepository;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// This is the general pattern that application services often follow when&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// interacted with event sourced domain model aggregates: load events,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// construct the aggregate with the events, run commands and save the newly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// created events.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; Result Run(OrderId orderId, &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;string&lt;/span&gt; paymentMethodNonce)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; events = _orderRepository.LoadEvents(orderId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; order = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Order(events);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; originalVersion = order.Version;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888&#34;&gt;// See below for what commands like these generally look like.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            order.UpdatePaymentDetails(paymentMethodNonce);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            order.ScheduleForPaymentProcessing();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            _orderRepository.Save(order, expectedVersion: originalVersion);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Result.Success();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// The repository uses the given version parameter to implement&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// optimistic concurrency control.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;catch&lt;/span&gt; (ConcurrencyException ex)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Result.Error(ex.Message);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Commands of event sourced aggregates don&amp;#39;t modify state directly.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Instead, they create the appropriate events and append them.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; UpdatePaymentDetails(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;string&lt;/span&gt; paymentMethodNonce)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; e = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; PaymentDetailsUpdatedEvent(_state.Id, paymentMethodNonce);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _events.Add(e);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _state.Apply(e);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;ddd-tools-for-implementing-business-logic&#34;&gt;DDD tools for implementing business logic&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/implementing-business-logic-ddd-part-2/concept-map.png&#34; alt=&#34;Concept map of the DDD patterns for implementing domain logic&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;These are the main concepts that we&amp;rsquo;ve explored, and how they relate to each other.&lt;/em&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>High Level System Analysis and Design with Domain-Driven Design</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/"/>
      <id>https://www.endpointdev.com/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/</id>
      <published>2026-04-06T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/pines-waterfall.webp&#34; alt=&#34;A wide waterfall cascades through a rocky, forested canyon surrounded by pine-covered hills under a cloudy sky.&#34;&gt;&lt;br&gt;
Photo by Zed Jensen, 2022.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is part 1 of a series of blog posts on Domain-Driven Design:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/&#34;&gt;High level system analysis and design with Domain-Driven Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/04/implementing-business-logic-ddd-part-2/&#34;&gt;Implementing business logic with Domain-Driven Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/05/designing-software-architecture-ddd-part-3/&#34;&gt;Designing software architecture with Domain-Driven Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2026/05/applying-it-in-practice-ddd-part-4/&#34;&gt;Applying Domain-Driven Design in Practice&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Domain-Driven Design&lt;/strong&gt; is an approach to software development that focuses on, &lt;a href=&#34;https://www.oreilly.com/library/view/domain-driven-design-tackling/0321125215/&#34;&gt;as Eric Evans puts it&lt;/a&gt;, &amp;ldquo;tackling the complexity in the heart of software&amp;rdquo;. And what is in the heart of software? The business domain in which it operates. Or more specifically: a &lt;strong&gt;model&lt;/strong&gt; of it, made of code. That is, the code that implements the business logic that comes into play when solving problems within the realm of a particular business activity.&lt;/p&gt;
&lt;p&gt;DDD is not just about writing code though. It&amp;rsquo;s a whole methodology that touches on business needs, requirements gathering, organizational dynamics, high level architectural design, and lower level patterns for implementing software intensive systems.&lt;/p&gt;
&lt;p&gt;As a result, DDD offers a treasure trove of concepts, patterns and tools that can be applied to any software project, regardless of the size and complexity.&lt;/p&gt;
&lt;p&gt;In this series of blog posts we&amp;rsquo;re going to explore many aspects of DDD. We will be following the structure laid out by &lt;a href=&#34;https://vladikk.com/&#34;&gt;Vlad Khononov&lt;/a&gt;&amp;rsquo;s excellent book on the topic &amp;ldquo;&lt;a href=&#34;https://www.oreilly.com/library/view/learning-domain-driven-design/9781098100124/&#34;&gt;Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy&lt;/a&gt;&amp;rdquo;. So you can think of this series as a summary of that book. An abridged version that can serve as a review for anybody who has read it; but also as an entry point for people who are new to DDD.&lt;/p&gt;
&lt;h3 id=&#34;table-of-contents&#34;&gt;Table of contents&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#high-level-system-analysis-and-design-with-domain-driven-design&#34;&gt;High level system analysis and design with Domain-Driven Design&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#table-of-contents&#34;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-1-analyzing-business-domains&#34;&gt;Section 1. Analyzing business domains&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#domains-and-subdomains&#34;&gt;Domains and subdomains&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#types-of-subdomains&#34;&gt;Types of subdomains&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#using-subdomains-to-make-strategic-decisions&#34;&gt;Using subdomains to make strategic decisions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-2-discovering-domain-knowledge&#34;&gt;Section 2. Discovering domain knowledge&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#the-ubiquitous-language&#34;&gt;The ubiquitous language&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#the-ubiquitous-language-as-a-model-of-the-domain&#34;&gt;The ubiquitous language as a model of the domain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#tools-for-capturing-the-ubiquitous-language&#34;&gt;Tools for capturing the ubiquitous language&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-3-managing-domain-complexity&#34;&gt;Section 3. Managing domain complexity&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#reasons-for-creating-bounded-contexts&#34;&gt;Reasons for creating bounded contexts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#bounded-contexts-vs-subdomains&#34;&gt;Bounded contexts vs subdomains&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-4-integrating-bounded-contexts&#34;&gt;Section 4. Integrating bounded contexts&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#cooperation-patterns&#34;&gt;Cooperation patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#customer-supplier-patterns&#34;&gt;Customer-supplier patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#separate-ways&#34;&gt;Separate ways&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#the-context-map&#34;&gt;The context map&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#the-ddd-high-level-design-concept-map&#34;&gt;The DDD high level design concept map&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;section-1-analyzing-business-domains&#34;&gt;Section 1. Analyzing business domains&lt;/h3&gt;
&lt;p&gt;It is a well understood notion that before writing code, we need to understand the problem we&amp;rsquo;re trying to solve. DDD is consistent with this notion and argues that developers need to, first and foremost, gain an understanding of the business that the software is being built for. To this end, DDD relies on three concepts: domains, subdomains and domain experts.&lt;/p&gt;
&lt;h4 id=&#34;domains-and-subdomains&#34;&gt;Domains and subdomains&lt;/h4&gt;
&lt;p&gt;In simple terms, the &lt;strong&gt;domain&lt;/strong&gt; of a business is what it does, its area of activity. For example, Starbucks is in the business of making coffee, Ford is in the business of making automobiles, AMC is in the business of movie theaters.&lt;/p&gt;
&lt;p&gt;Of course, analyzing a business as a single integrated whole can be unmanageable. That&amp;rsquo;s where subdomains come in. &lt;strong&gt;Subdomains&lt;/strong&gt; are the different divisions within a domain. Starbuck&amp;rsquo;s domain for example, is making coffee. But there are many smaller parts that make up that business and allow it to be successful. There&amp;rsquo;s of course, the subdomain of coffee preparation. But there&amp;rsquo;s also real estate management to find and secure good locations, there&amp;rsquo;s inventory management and logistics, there&amp;rsquo;s marketing, there&amp;rsquo;s human resources, etc. All these are the subdomains that make up the overall business of Starbucks.&lt;/p&gt;
&lt;p&gt;Depending on the business and on the project, these will vary greatly in granularity. And you can also decompose subdomains further and discover new fine-grained subdomains nested within more coarse-grained ones. The sizes and nesting levels can be very different from business to business, so it can be difficult to clearly delineate where a subdomain ends and another one starts, which activities belong to one or the other. One good rule of thumb to keep in mind is that generally, subdomains encapsulate a set of coherent, closely related use cases. That is, use cases that involve the same set of closely related actors, business entities and/or data.&lt;/p&gt;
&lt;p&gt;And finally, we have &lt;strong&gt;domain experts&lt;/strong&gt;. As the name suggests, these are the people within the organization who have intimate knowledge of the business, or certain areas of it. They are the subject matter experts. Usually they are the ones who identify the problems and come up with requirements. Developers need to rely on domain experts to gain the necessary understanding to be able to produce useful software solutions.&lt;/p&gt;
&lt;p&gt;So, when approaching software projects, DDD suggests that developers work closely with domain experts in order to learn from them about the business domain and subdomains. After all, it is their mental models and understanding that will be modeled and implemented in code.&lt;/p&gt;
&lt;h4 id=&#34;types-of-subdomains&#34;&gt;Types of subdomains&lt;/h4&gt;
&lt;p&gt;With the help of domain experts, developers can identify subdomains, understand their business value and how they fit within the overall business strategy. This is very important because it helps making some initial architecturally significant decisions. Namely, the general approach to solving the problems in the subdomains, how much to invest, how to organize development teams, etc. The main objective in this analysis stage is to identify the subdomains and whether they fall into one of three types: core, generic, and supporting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Core subdomains&lt;/strong&gt; are the activities of the business with highest value. The ones that confer differentiation in the market and a competitive advantage. They are the business&amp;rsquo; raison d&amp;rsquo;être. For example, for Google, their search engine is a core subdomain. For Ford, their automotive engineering area would be a core subdomain. Core subdomains are generally the most complex parts of the business. They are constantly evolving and improving, and the company is compelled to invest heavily in them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Generic subdomains&lt;/strong&gt; are also very complex. However, they are not business differentiators. Instead, these are the areas of the business that all organizations handle in the same way. Think accounting, a ticketing system, an online storefront. There&amp;rsquo;s no pressure to innovate in these areas, so the solutions are very stable and evolution is slow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Supporting subdomains&lt;/strong&gt; on the other hand, do not provide any competitive advantage, nor are they very complex. They are however, necessary because they support the core business activities, and are fairly unique. The solutions to problems in these areas usually take the form of &lt;a href=&#34;https://en.wikipedia.org/wiki/Create,_read,_update_and_delete&#34;&gt;CRUD&lt;/a&gt; or &lt;a href=&#34;https://en.wikipedia.org/wiki/Extract,_transform,_load&#34;&gt;ETL&lt;/a&gt; oriented activities. Imagine for example populating a data warehouse, translating transactional business data into a format appropriate for analytics and business intelligence in a manufacturing corporation. Or maybe the digitization and storage of a registry of court documents for a law firm. These are behind the scenes activities that support the organizations&amp;rsquo; main businesses, but their business logic complexity is not high, and they don&amp;rsquo;t really represent big selling points for the company.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s worth noting as well, that there may be subdomains where software solutions are not appropriate, even if they are highly complex core subdomains. They are still part of the business so it&amp;rsquo;s worth identifying and considering for high level architectural design decisions. If anything, to know what parts of the business the planned software system should and should not focus on. You could have a restaurant, for example, who prides itself in having the best desserts in the city. For their business, the recipe development activities constitute a core subdomain. This is dependent on the art and craftsmanship of the chefs. Not an area in which software solutions could help a whole lot.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also worth noting that, just as organizations&amp;rsquo; business strategies are dynamic, so too can be their subdomain distribution. Today&amp;rsquo;s generic subdomain can be tomorrow&amp;rsquo;s core subdomain, and so on. For example, imagine a big retail store chain that, up until now, managed its inventory in an industry standard way. But it has grown so much that the standard way of doing things has become a bottleneck for them. So they design a new procedure for highly efficient inventory management, and that gives them an edge against competitors. Inventory management started as a generic subdomain for them, but due to an ever evolving business strategy, it became a core subdomain.&lt;/p&gt;
&lt;p&gt;The knowledge of subdomain types can also help the analysis when dividing the business and discovering subdomains. If you find yourself recursively finding more and more fine-grained subdomains from already identified ones, a good time to stop recursing is when you find that a generic or supporting subdomain, when broken down, reveals only new finer-grained generic or supporting subdomains. The reasoning for this is simple: this analysis activity is most valuable when trying to identify core subdomains. When there are no more core subdomains to be found within a particular area of the business, then it&amp;rsquo;s probably not worth it to keep digging deeper.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/decomposing-a-generic-subdomain.png&#34; alt=&#34;Decomposing a generic subdomain&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Here we have an example of a hypothetical help desk system subdomain, part of a customer service subdomain, being decomposed and revealing that it only contains generic subdomains.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In summary, these are the main characteristics of the three types of subdomains:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Core subdomains&lt;/strong&gt; have high complexity, provide competitive advantage, and change and evolve frequently.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generic subdomains&lt;/strong&gt; have high complexity, do not provide competitive advantage and change overtime, although at a slower pace than core subdomains.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Support subdomains&lt;/strong&gt; have low complexity, do not provide competitive advantage and are the slowest to change.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/subdomain-types-complexity-vs-differentiation.png&#34; alt=&#34;Subdomain types complexity vs differentiation&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is where each type of subdomain fall when considering their business logic complexity and business differentiation.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;using-subdomains-to-make-strategic-decisions&#34;&gt;Using subdomains to make strategic decisions&lt;/h4&gt;
&lt;p&gt;Like I&amp;rsquo;ve already alluded to, armed with this knowledge, DDD practitioners are ready to start making some higher level architectural decisions. Depending on the type of subdomain that a problem belongs to, DDD has specific prescriptions on how to handle the implementation of software solutions for them.&lt;/p&gt;
&lt;p&gt;When working on &lt;strong&gt;core subdomains&lt;/strong&gt;, that&amp;rsquo;s where we want to make the biggest investments. We deploy the most advanced engineering tools, patterns and practices. This is to make sure that the software is efficient and high quality, and also easy to maintain and evolve. This is necessary because core subdomains have to evolve rapidly by nature, if the business is to maintain competitive advantage. Software solutions that operate within the context of core subdomains have to be implemented by high skill and high trust teams. Either in-house, or via trusted development partners, working hand in hand with domain experts.&lt;/p&gt;
&lt;p&gt;Problems in &lt;strong&gt;generic subdomains&lt;/strong&gt;, by nature of their business logic being very complex but also very common, are likely to have already been solved. For these types of problems, DDD recommends against implementing custom software, and instead buying and/or adopting tried and true, industry standard, off-the-shelf solutions. Their implementation and integration can be outsourced or handled by less specialized or skilled teams. The change management of these solutions is simple, as they get delivered generally via patches and updates.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;support subdomains&lt;/strong&gt;, whose business logic is generally simple but uncommon, it is less likely that off-the-shelf solutions would be available. So software addressing problems in these subdomains will most likely have to be implemented as custom solutions. Due to their low complexity though, they can be easily outsourced, or handled by more junior team members. They can also be handled with &lt;a href=&#34;https://en.wikipedia.org/wiki/Rapid_application_development&#34;&gt;RAD&lt;/a&gt;, low-to-no-code technologies, since often times they are little more than ETL and pure CRUD applications.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a table to summarizes the differences between the types of subdomains:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Type of subdomain&lt;/th&gt;
          &lt;th&gt;Core&lt;/th&gt;
          &lt;th&gt;Generic&lt;/th&gt;
          &lt;th&gt;Supporting&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Competitive advantage&lt;/td&gt;
          &lt;td&gt;Yes&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Complexity&lt;/td&gt;
          &lt;td&gt;High&lt;/td&gt;
          &lt;td&gt;High&lt;/td&gt;
          &lt;td&gt;Low&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Rate of change&lt;/td&gt;
          &lt;td&gt;High&lt;/td&gt;
          &lt;td&gt;Medium&lt;/td&gt;
          &lt;td&gt;Low&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Implementation&lt;/td&gt;
          &lt;td&gt;Custom development&lt;/td&gt;
          &lt;td&gt;Buy/adopt off-the-shelf&lt;/td&gt;
          &lt;td&gt;Custom development&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Team composition&lt;/td&gt;
          &lt;td&gt;In-house/partners&lt;/td&gt;
          &lt;td&gt;Can outsource&lt;/td&gt;
          &lt;td&gt;Can outsource&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Skill level&lt;/td&gt;
          &lt;td&gt;High&lt;/td&gt;
          &lt;td&gt;High/regular&lt;/td&gt;
          &lt;td&gt;Low&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Investment&lt;/td&gt;
          &lt;td&gt;High&lt;/td&gt;
          &lt;td&gt;Medium&lt;/td&gt;
          &lt;td&gt;Low&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Problems&lt;/td&gt;
          &lt;td&gt;Interesting&lt;/td&gt;
          &lt;td&gt;Solved&lt;/td&gt;
          &lt;td&gt;Simple&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;section-2-discovering-domain-knowledge&#34;&gt;Section 2. Discovering domain knowledge&lt;/h3&gt;
&lt;p&gt;After identifying the business domain and categorizing the various subdomains that compose it, we have a bird&amp;rsquo;s-eye view of the business. This is good enough to get started and make high level architectural decisions of potential software solutions to problems within these domains. To actually build the software though, we need much more than that. In order to gain a thorough understanding of the business logic, and be able to eventually model and implement it in code, DDD proposes the &lt;strong&gt;ubiquitous language&lt;/strong&gt; as a tool.&lt;/p&gt;
&lt;h4 id=&#34;the-ubiquitous-language&#34;&gt;The ubiquitous language&lt;/h4&gt;
&lt;p&gt;The &lt;strong&gt;ubiquitous language&lt;/strong&gt; is DDD&amp;rsquo;s tool for knowledge sharing, effective communication and software modeling. In simple terms, the ubiquitous language is the language of the business. It&amp;rsquo;s the language that domain experts use on a day to day basis to talk and reason about the business.&lt;/p&gt;
&lt;p&gt;For a software project to be successful, it&amp;rsquo;s essential that engineers and stakeholders understand each other. They have to be aligned when it comes to the meanings of the core concepts of the business domain. That&amp;rsquo;s why it&amp;rsquo;s so important that everybody uses the ubiquitous language for all project related communications, requirements, documentation, face to face discussions, and even code itself. It all needs to share the same language.&lt;/p&gt;
&lt;p&gt;Engineers will need to interact with domain experts in order to learn about the business domain and its rules. They need to acquire the necessary knowledge that allows them to implement these rules in the software. During these interactions, they converse using the ubiquitous language. However, the knowledge transfer is not always strictly unidirectional. Yes, engineers have to learn from the domain experts. But also, through conversations and questioning, engineers can help domain experts deepen and flesh out their own understanding about their domains.&lt;/p&gt;
&lt;p&gt;A classic example of this is when domain experts focus too much on the &amp;ldquo;happy paths&amp;rdquo; of given business processes. Then, through discussions with developers, on account of the inherent precision that software demands, they are forced to consider more edge cases, better specify ambiguous terms and fill out gaps in their understanding.&lt;/p&gt;
&lt;p&gt;This deepening of knowledge feeds back into the ubiquitous language and improves it. Making it more insightful and more precise. This means that, throughout the life cycle of a project, the ubiquitous language should keep evolving, expanding and refining. This allows it to continuously improve as an effective model of the business domain, for the problem that it&amp;rsquo;s trying to solve. Indeed, just like code is a model of the business domain, so too is the ubiquitous language.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/traditional-knowledge-sharing-flow.png&#34; alt=&#34;Traditional knowledge sharing flow&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A classic pitfall in software projects is a manner of communication and knowledge sharing where developers are various steps removed from the domain experts. Consistent use of the ubiquitous language addresses this.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;the-ubiquitous-language-as-a-model-of-the-domain&#34;&gt;The ubiquitous language as a model of the domain&lt;/h4&gt;
&lt;p&gt;Through consistent use of the ubiquitous language, developers are able to obtain a deeper understanding of the domain, the problems we&amp;rsquo;re trying to solve, the reasoning behind the requirements and the mental model of the domain experts. This allows the construction of effective software solutions, with deep business insight, that go beyond simply translating requirements into code. If the implementation models the business domain effectively, there is potential for it to evolve with the business and better adapt as requirements change; and we reduce the risk of missing edge cases that may not be obvious to domain experts.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/knowledge-sharing-in-ddd.png&#34; alt=&#34;Knowledge sharing under DDD&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Under DDD, developers and domain experts develop the ubiquitous language, which then informs the implementation.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Effectively, when we build a ubiquitous language, we&amp;rsquo;re building a model of the domain. A model that reflects the relevant business entities, their behavior, and relationships. A model that will be eventually implemented into code, but also a model that needs to be understood by all stakeholders, regardless of their technical level. So it needs to be precise and rigorous, but also understandable. As such, in order to be useful, the ubiquitous language needs to respect certain restrictions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It must not include technical jargon like programming languages, constructs or frameworks; nor mention specific system structures like database tables, servers or programs. It is the language of the business and it needs to be understandable by non technical folks.&lt;/li&gt;
&lt;li&gt;It must avoid using the same term for different concepts. In conversation with humans, a lot of the meaning is extracted from the context. In software, not so much. Just like you can&amp;rsquo;t have two classes with the same name within the same namespace, the ubiquitous language cannot allow such ambiguities.&lt;/li&gt;
&lt;li&gt;It should also avoid using different words for the same concept. The classic example for this is words like users, accounts, customers, visitors. They all refer to closely related concepts which might actually have differences. The problem with these overlapping terms is that those differences are obscured by the notion of them &amp;ldquo;being the same&amp;rdquo; and being used interchangeably. For usage within the ubiquitous language, it&amp;rsquo;s best to be precise and clearly delineate terms and definitions. Eliminate what&amp;rsquo;s redundant, be strict with definitions.&lt;/li&gt;
&lt;li&gt;It should avoid including extraneous details. Just like code needs to include only what&amp;rsquo;s needed to solve the problem at hand and nothing else (in order to avoid accidental complexity); the ubiquitous language must not be polluted with details from outside of the area of business activity that it represents. That can create confusion and unnecessary cognitive load.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/model-translations.png&#34; alt=&#34;Classic model translations&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The ubiquitous language represents a unified model of the domain. This is in contrast to a more traditional process where domain knowledge gets &amp;ldquo;translated&amp;rdquo; multiple times before turning into code.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;tools-for-capturing-the-ubiquitous-language&#34;&gt;Tools for capturing the ubiquitous language&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&#34;https://agilemanifesto.org/&#34;&gt;Agile Manifesto&lt;/a&gt; declares: &amp;ldquo;Individuals and interactions over processes and tools&amp;rdquo;. And of course, when it comes to the ubiquitous language, direct interaction and conversations between engineers, domain experts and other stakeholders reigns supreme. That&amp;rsquo;s when it can be most useful. It can also be useful, however, to capture the ubiquitous language in documentation and even in runnable form.&lt;/p&gt;
&lt;p&gt;A glossary of terms is a great asset for keeping a ubiquitous language. A wiki is a good place to put this. Definitions for key concepts in the business like entities, processes and rules can be captured here. The only caveat is that documentation is static by nature, while the ubiquitous language is continually evolving. So, great care needs to be taken to keep the wiki updated at all times to reflect the latest and most complete understanding of the domain. This should not be relegated to or gate-keeped by only certain people; it should be a team effort where everyone contributes.&lt;/p&gt;
&lt;p&gt;An automated acceptance tests suite, written using &lt;a href=&#34;https://en.wikipedia.org/wiki/Behavior-driven_development&#34;&gt;Behavior Driven Development&lt;/a&gt; frameworks, like &lt;a href=&#34;https://cucumber.io/&#34;&gt;Cucumber&lt;/a&gt;, is also a great way of capturing the ubiquitous language. The advantage of these tests is that they are written in plain human-readable language, not code. And while it may be far fetched to think that non technical domain experts would be capable of writing and maintaining such tests, they certainly can read and understand them, which is a great boon. These tests speak the language they understand: the language of the business.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/cucumber.png&#34; alt=&#34;Cucumber tests&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is what Cucumber tests look like.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;By nature of being executable and tied closely to the implementation code, there is less chance that they become out of date. This can happen more easily with static documentation written in a wiki. The disadvantage is that they require much more effort. But for the right kind of project, one where business logic is very complex or the scope is very wide, they can be very well worth the cost.&lt;/p&gt;
&lt;h3 id=&#34;section-3-managing-domain-complexity&#34;&gt;Section 3. Managing domain complexity&lt;/h3&gt;
&lt;p&gt;We can attempt to model an entire business domain with one big ubiquitous language, but sometimes that&amp;rsquo;s impossible. Especially so for businesses of a certain size, you will inevitably find inconsistencies and conflicts between the mental models of different domain experts. The simplest example of this scenario is when different experts from different areas of the organization have the same word to describe different concepts. Or when they look at the same business entity with different levels of detail.&lt;/p&gt;
&lt;p&gt;When the different mental models are valid, and these inconsistencies are legitimate and cannot be reconciled, the solution is to follow the divide and conquer principle and decompose the language into separate ones, each one working within its own bounded context.&lt;/p&gt;
&lt;p&gt;Simply put, a &lt;strong&gt;bounded context&lt;/strong&gt; is the context within which a ubiquitous language, and the model it represents, operate, have meaning and are useful. Sure enough, just like a domain can be divided into subdomains, a ubiquitous language can be decomposed into smaller languages, each with its own context, to model different parts of the business. While we can attempt to capture an entire business domain with a single model, this is not advisable in the case of complex systems. It&amp;rsquo;s better to split up the model into smaller, more precise ones, tailored to work on specific problem domains.&lt;/p&gt;
&lt;h4 id=&#34;reasons-for-creating-bounded-contexts&#34;&gt;Reasons for creating bounded contexts&lt;/h4&gt;
&lt;p&gt;Like mentioned before, different domain experts having conflicting mental models is a clear indication that a division in the model needs to happen. However, that only tells us half of the story. There are other indicators that point to when and where further division should happen:&lt;/p&gt;
&lt;p&gt;First of all, there is of course, size. Size by itself is not a deciding factor, but it is something to consider and balance. Fewer, bigger models can help keep the overall environment simpler, but if they become too big they can become unmanageable and prone to corruption. A higher number of smaller models keep cognitive load low, but you run the risk of exploding integration and management complexity. The better principles to keep in mind here are coupling and cohesion. Beware of separating closely tied use cases, that deal with similar actors, entities and data.&lt;/p&gt;
&lt;p&gt;System-level nonfunctional requirements also play a role in dividing a model into separate bounded contexts. For example, you might need to decouple the life cycle of several software components. Have them be developed, evolved, versioned and deployed independently. You might also need them to scale separately. You might even need to use completely different technology stacks, whatever is appropriate for the task at hand. This means that bounded contexts generally get implemented as individual services and/or applications. That is, as individual runtime components.&lt;/p&gt;
&lt;p&gt;The organization&amp;rsquo;s composition also plays a role when designing bounded contexts. The general rule is that a given ubiquitous language, the model it represents and the bounded context is lives in, must be owned by a single team. In software, high ownership, cohesion and consistency are desirable traits. Having a single team own and maintain a particular component foments these. Single ownership also helps reduce communication overhead, and prevents people from stepping on one another&amp;rsquo;s toes. Of course, a single team can own multiple bounded contexts, what cannot happen is one bounded context being owned by multiple teams.&lt;/p&gt;
&lt;p&gt;Indeed, in bounded contexts, we have the tools we need to make strategic decisions related to the decomposition of software systems into architecturally significant components or modules. With bounded contexts, we are able to specify the physical and ownership boundaries of these components.&lt;/p&gt;
&lt;h4 id=&#34;bounded-contexts-vs-subdomains&#34;&gt;Bounded contexts vs subdomains&lt;/h4&gt;
&lt;p&gt;Bounded contexts and subdomains are closely related concepts, but there&amp;rsquo;s one key distinction: &lt;strong&gt;subdomains&lt;/strong&gt; are discovered, while &lt;strong&gt;bounded contexts&lt;/strong&gt; are designed. Subdomains are useful because they help us understand the business strategy. Splitting the business domain into smaller problem domains is useful because it can break down a complex whole into smaller and more manageable parts.&lt;/p&gt;
&lt;p&gt;Depending on the situation, it is certainly possible to end up with a set of bounded contexts that align one-to-one with the business subdomains. However, this is not mandatory. We can develop a solution with a single bounded context that spans multiple subdomains; the same way that we can decompose the problem into many bounded contexts, some of which operate within the same particular subdomain.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/subdomains-and-bounded-contexts.png&#34; alt=&#34;Subdomains and bounded contexts&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Bounded contexts are closely related to subdomains, but aren&amp;rsquo;t tied to them. They are flexible and can be organized in many ways.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Each bounded context becomes a separate major architectural component. That is, a (micro) service, a project, an application. When we have a component that spans multiple subdomains, programming language organizational structures like namespaces or modules can be used to logically separate the subdomains within.&lt;/p&gt;
&lt;h3 id=&#34;section-4-integrating-bounded-contexts&#34;&gt;Section 4. Integrating bounded contexts&lt;/h3&gt;
&lt;p&gt;For a system to function, its components need to interact with each other. So, once we have decomposed the problem domain into separate bounded contexts, we need to decide their relationship and integration strategies. This need for interaction between them implies that there are touch points between bounded contexts. We call them &lt;strong&gt;contracts&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;These contracts are necessary because each bounded context contains its own version of the world. That is, its own model and ubiquitous language. In order to integrate, some level of translation needs to happen. They need to be adapted to one another. The contracts define these adaptations.&lt;/p&gt;
&lt;p&gt;Domain-Driven Design offers various patterns that are useful for defining contracts between bounded contexts. The decision to use one pattern vs the other depends greatly on the nature of the teams tackling the project. Depending on the teams&amp;rsquo; relationship, we can put the patterns in one of three categories: cooperation, customer-supplier, and separate ways.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/collaboration-spectrum.png&#34; alt=&#34;Collaboration spectrum&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Integration patterns are determined mainly by the level of cooperation between the teams that own the interacting bounded contexts.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;cooperation-patterns&#34;&gt;Cooperation patterns&lt;/h4&gt;
&lt;p&gt;When the components that need to interact are owned by teams which are in close communication, work well together, and whose goals are aligned, &lt;strong&gt;cooperation&lt;/strong&gt; patterns can be applied.&lt;/p&gt;
&lt;p&gt;If the teams meet these criteria, a &lt;strong&gt;partnership&lt;/strong&gt; model can be implemented. This is where the integration is managed in an ad-hoc manner. Both teams work together to define the API through which their components interact and that&amp;rsquo;s that. Whenever changes happen on either side, the other team learns about it quickly and adjusts their code right away. Continuous integration is a great tool here, as breakages are immediately apparent, closing down the feedback loop.&lt;/p&gt;
&lt;p&gt;Sometimes, when different bounded contexts need to implement the same functionality, it makes more sense to develop this functionality once and package it as a reusable library, or a sub-module within a shared repository. This is what DDD calls a &lt;strong&gt;shared kernel&lt;/strong&gt;. A shared kernel is effectively a bounded context of its own, one that is statically linked to other components and implements the logic that others depend on.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/shared-kernel.png&#34; alt=&#34;Shared kernel&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A shared kernel is its own bounded context, but also &amp;ldquo;belongs&amp;rdquo; to multiple other bounded contexts.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The shared kernel integration pattern needs to be used carefully though, as it creates tight coupling between the components that use it. If the involved bounded contexts are owned by different teams, then this shared kernel that emerges violates the DDD principle of bounded contexts having single-team ownership. This is something to watch out for, as bad team synergy can produce problems during development and maintenance.&lt;/p&gt;
&lt;p&gt;When using a shared kernel, the quality of the communication and the coordination between the teams has to strike the right balance: when they aren&amp;rsquo;t strong enough to support a partnership model, but not so weak that the shared kernel would become more trouble than it&amp;rsquo;s worth.&lt;/p&gt;
&lt;p&gt;The alternative to a shared kernel is the duplication of the logic across multiple components. When deciding whether to apply this pattern, the costs of duplication vs coordination need to be considered. That is, the cost of implementing the same logic within multiple components and its subsequent changes, vs the cost of changes in the shared kernel propagating into dependent components and coordinating with the owners of each. When it is cheaper to just duplicate the logic, shared kernel should not be applied.&lt;/p&gt;
&lt;p&gt;This means that complicated models that change frequently, like those of core subdomains, are good candidates for shared kernel. Shared kernels allow complexity to be encapsulated and exposed through stable contracts. This means that frequent changes are easier to contain. Frequent changes in logic that&amp;rsquo;s highly complex and also repeated in multiple places, quickly becomes much more expensive to maintain.&lt;/p&gt;
&lt;p&gt;Another good scenario for applying a shared kernel is when refactoring a legacy monolithic system into more separated modules. The legacy system can become the shared kernel during the time that modules are being extracted from it but not yet fully decoupled.&lt;/p&gt;
&lt;h4 id=&#34;customer-supplier-patterns&#34;&gt;Customer-supplier patterns&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Customer-supplier&lt;/strong&gt; patterns establish a relationship between components where one (the provider, who is &amp;ldquo;upstream&amp;rdquo;), provides a service to another (the consumer, who is &amp;ldquo;downstream&amp;rdquo;). These types of patterns emerge when the teams who own the involved bounded contexts are not in close collaboration and have their own independent goals.&lt;/p&gt;
&lt;p&gt;One form of customer-supplier integration is the &lt;strong&gt;conformist&lt;/strong&gt; pattern. This happens when the supplier defines the integration contract/API, using its own language, concepts and model, and the consumer accepts it. This can happen when the upstream service has a well established or industry-standard model. Or maybe the model is &amp;ldquo;good enough&amp;rdquo; for the consumer to interact with directly. Organizational politics may also be a reason for this type of integration, where there is an imbalance of power favoring the supplier&amp;rsquo;s team, and they get to impose their model or can&amp;rsquo;t be bothered to adapt it.&lt;/p&gt;
&lt;p&gt;Sometimes, the consumer won&amp;rsquo;t accept the supplier&amp;rsquo;s model. In that case, an &lt;strong&gt;anticorruption layer&lt;/strong&gt; can be created. An anticorruption layer is implemented by the consumer and translates the supplier&amp;rsquo;s model into its own. This allows the consumer to use the supplier&amp;rsquo;s service without polluting its model with extraneous concepts.&lt;/p&gt;
&lt;p&gt;Anticorruption layers can be good solutions for the following scenarios:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When the consumer represents a core subdomain. The consumer is solving hard and interesting problems, so it&amp;rsquo;s best to protect its model with the ACL.&lt;/li&gt;
&lt;li&gt;When the supplier&amp;rsquo;s model is messy and inconvenient. The ACL can protect the consumer&amp;rsquo;s model from having to contend with a mess of extraneous concepts.&lt;/li&gt;
&lt;li&gt;When the supplier&amp;rsquo;s contract changes frequently. The ACL can protect the consumer from those changes, encapsulating them, allowing it to be less volatile.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A third pattern represents an anticorruption layer of sorts, only built on the supplier&amp;rsquo;s side. DDD calls this the &lt;strong&gt;open-host service&lt;/strong&gt; pattern. Here, the upstream service comes up with a new language, separate from its own, tailored to its consumers&amp;rsquo; convenience, and exposes that as its contract. This public protocol is called a &amp;ldquo;published language&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/open-host-service.png&#34; alt=&#34;Open-host service&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;An open-host service provides a protocol for consumers to interact with. It can also support multiple versions of this protocol simultaneously.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;An open-host service applies to similar scenarios as an anticorruption layer. Decoupling a service&amp;rsquo;s internal model from its integration model frees it up for continuous evolution without fear of breaking its consumers. Another advantage of this is that multiple versions of the published language can be exposed, affording clients options on what to support and gradually migrate if they so choose.&lt;/p&gt;
&lt;h4 id=&#34;separate-ways&#34;&gt;Separate ways&lt;/h4&gt;
&lt;p&gt;Sometimes, the right decision is to not integrate at all. I alluded to this outcome back when discussing the pitfalls of the shared kernel. Sometimes duplication, &lt;a href=&#34;https://refactoring.guru/smells/duplicate-code&#34;&gt;in spite of how bad it smells&lt;/a&gt;, may be the most cost effective solution to a given situation. So the teams go their &lt;strong&gt;separate ways&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This can happen when the involved teams cannot collaborate effectively for whatever reason. It could be due to geographical, timezone, or organizational issues.&lt;/p&gt;
&lt;p&gt;This can also be a good solution when the repeated logic belongs in a generic subdomain, and it&amp;rsquo;s easy to integrate. For example consider a logging framework. Exposing that kind of functionality in a service for others to consume, in most cases would be much more trouble than just including some third party library or package.&lt;/p&gt;
&lt;p&gt;It may also be that the models being integrated are just so different that they are fundamentally incompatible. It may be cost-prohibitive for collaboration or customer-supplier patterns to be applied; and duplication again, is cheaper.&lt;/p&gt;
&lt;p&gt;Going separate ways can be dangerous when we&amp;rsquo;re talking about core subdomains though. So we have to tread carefully in those scenarios. Remember that models that represent core subdomain should be implemented in the most effective and efficient ways, with few shortcuts and minimized technical debt.&lt;/p&gt;
&lt;h4 id=&#34;the-context-map&#34;&gt;The context map&lt;/h4&gt;
&lt;p&gt;The &lt;strong&gt;context map&lt;/strong&gt; can be a useful tool for high level design as it plots all the major bounded contexts (i.e. modules, components, subsystems) that we&amp;rsquo;ve designed and their interaction patterns.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/context-map.png&#34; alt=&#34;Context map&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Here&amp;rsquo;s a context map which captures the various bounded contexts that compose a big system and their interactions. The arrows point to the upstream component in the relationship. &amp;ldquo;ACL&amp;rdquo; denotes an anticorruption layer and &amp;ldquo;OHS&amp;rdquo; represents an open-host service.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Of course, they can also offer valuable insight into organizational dynamics, as team composition and relationships with others are intrinsic parts of the discussion when talking about bounded contexts. For example, it can show teams that prefer to collaborate closely or at a healthy distance. I can also show problematic components, which are surrounded by anticorruption layers or have had their ties completely cut via a separate ways approach.&lt;/p&gt;
&lt;p&gt;As with any document, they run the risk of becoming stale as the system evolves. So it should be a team-wide responsibility to keep it up to date. Each team taking care of their own components and their integration points.&lt;/p&gt;
&lt;h3 id=&#34;the-ddd-high-level-design-concept-map&#34;&gt;The DDD high level design concept map&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2026/04/high-level-system-analysis-and-design-ddd-part-1/concept-map.png&#34; alt=&#34;The DDD high level design concept map&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;These are the main concepts that we&amp;rsquo;ve explored so far, and how they relate to each other.&lt;/em&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Choosing Between SaaS and a Custom Website</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2021/05/choosing-between-saas-and-custom-website/"/>
      <id>https://www.endpointdev.com/blog/2021/05/choosing-between-saas-and-custom-website/</id>
      <published>2021-05-18T00:00:00+00:00</published>
      <author>
        <name>Greg Hanson</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2021/05/choosing-between-saas-and-custom-website/drafting.jpg&#34; alt=&#34;&#34;&gt;
&lt;a href=&#34;https://unsplash.com/photos/-FPFq_trr2Y&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://unsplash.com/@d_mccullough&#34;&gt;Daniel McCullough&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So you need a website, but you’re not sure whether you want to pay for a website provided as software as a service (SaaS), or build a custom website and host it.&lt;/p&gt;
&lt;p&gt;The options are plentiful, as are the reasons for building a website in the first place. The purpose of this article is not to put forward specific packages or providers for consideration, rather I want to discuss how you might make that decision more objectively. Also, this discussion is for a commercial website, not a personal website or blog.&lt;/p&gt;
&lt;p&gt;Here at End Point, we receive many inquiries asking for new websites. We are well equipped to help you work through this process, and are happy to do so. But for those of you who want to “go it alone”, or if you just want to be better prepared before giving us a call, read on for some things to consider as you make a pros &amp;amp; ​cons list for SaaS vs. custom websites.&lt;/p&gt;
&lt;p&gt;While deciding between a SaaS offering and a custom build carries unlimited considerations, here are a few main points to help you narrow down the field.&lt;/p&gt;
&lt;h3 id=&#34;experience&#34;&gt;Experience&lt;/h3&gt;
&lt;p&gt;The foremost factor that you should consider for your pros &amp;amp; cons list is your experience. This may come as a little bit of a shock, as you might think that budget would be the top consideration. Budget is very important, but it is largely determined by your experience with your chosen technology. Don’t think for a minute that either SaaS or custom development cannot consume almost any budget, especially when you are working with an unfamiliar technology.&lt;/p&gt;
&lt;p&gt;So before budget, comes experience. Experience can be futher divided into experience with your business and experience with websites in general. Here are a few questions to start off with.&lt;/p&gt;
&lt;p&gt;In your industry, are you a:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Rookie&lt;/li&gt;
&lt;li&gt;Veteran&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;How long have you owned this business?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;1–3 years&lt;/li&gt;
&lt;li&gt;3+ years&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Does this business have an existing website?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;No&lt;/li&gt;
&lt;li&gt;Yes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If yes, is it currently SaaS or custom-developed?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;SaaS&lt;/li&gt;
&lt;li&gt;Custom-developed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you answered “1” to more than half of the above questions, it’s more likely that a SaaS system would be a better fit for you. If you mostly answered “2”, you may want to lean towards custom. These questions are not definitive, but are meant to help you decide where to focus your research.&lt;/p&gt;
&lt;h3 id=&#34;budget&#34;&gt;Budget&lt;/h3&gt;
&lt;p&gt;In general, if your budget is under $10,000 then SaaS is most likely the choice for you. Some exceptions here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You are a talented web programmer.&lt;/li&gt;
&lt;li&gt;Your brother, sister, friend, or cousin is a talented web programmer.&lt;/li&gt;
&lt;li&gt;You or your developer friend have enough time to devote to this endeavor. You will need a lot.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Custom websites are no longer appropriate for the average business owner with less than a $10k minimum start up cost, as well as a $1–3k monthly budget for ongoing improvements and maintenance. Currently, droves of talented programmers are working for SaaS companies, building tools for you to use to build your own website. In fact, SaaS offerings are so much cheaper and more widely supported that building a custom site for the average small business usually makes little sense.&lt;/p&gt;
&lt;p&gt;If your budget is $10k or more, you are probably capable of funding a custom website. However, this alone should not rule out a SaaS website. SaaS offerings come in many shapes and sizes, and many are fully capable of running enterprise-level websites!&lt;/p&gt;
&lt;h3 id=&#34;how-does-your-business-make-money&#34;&gt;How does your business make money?&lt;/h3&gt;
&lt;p&gt;One good case for using a custom website is if your company offers a niche product. Most SaaS offerings are based on popular business or product templates, so they might not fit well if you need to break from the mold.&lt;/p&gt;
&lt;p&gt;For example, do you sell clothing, movies, or tools? There are great SaaS offerings out there that can have your website up and running in less than a day. Do you provide a service that uses multiple providers, working in different capacities and for different rates depending on the level of service provided? You may need a custom application! The further from commonplace your products or services are, the more likely it is that you will need a custom application.&lt;/p&gt;
&lt;p&gt;Again, all of this advice is general, and there are exceptions to every rule. SaaS choices still require customizing to fit your business model. None of them will fit perfectly, and you will usually need to configure and add plugins to make your site conform to your business practices. SaaS offerings typically have a wide range of plugins and customizations to accommodate this. Those extras usually cost extra too.&lt;/p&gt;
&lt;p&gt;While SaaS sites do provide a great starting point with a plethora of potential plugins, in many cases plugins are supplied by 3rd parties. That means that if you have problems with the plugin, you will need to deal with that provider separate from the main service provider.&lt;/p&gt;
&lt;p&gt;A lot of time and money can be spent trying to make diverse systems work together. SaaS can be more “turn-key” in the beginning, but especially for more complex websites, it can end up costing more than if you created a custom site in the beginning.&lt;/p&gt;
&lt;h3 id=&#34;what-vendors-does-your-company-work-with&#34;&gt;What vendors does your company work with?&lt;/h3&gt;
&lt;p&gt;Vendors are sometimes overlooked when trying to decide on what type of website will be needed. Here are some questions which might complicate your website:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do you use a 3rd party to ship or fulfill your products?&lt;/li&gt;
&lt;li&gt;Do you sell your products on Amazon, Google Shopping, or other providers?&lt;/li&gt;
&lt;li&gt;Do you use a vendor to provide your products to you?&lt;/li&gt;
&lt;li&gt;Do you assemble the product after receiving parts from different vendors?&lt;/li&gt;
&lt;li&gt;Does your company use any software for customer management, accounting, or shipping?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your answer is yes to any of these questions, you need to know what platform each of those vendors or platforms use, and if it is compatible with the website you are planning to build. Almost all business done today involves the exchange of information between you, your customer, and your vendors.&lt;/p&gt;
&lt;p&gt;That exchange of information usually takes place through an Application Programming Interface (API). If you aren’t familiar with APIs, here’s a &lt;a href=&#34;https://www.freecodecamp.org/news/what-is-an-api-in-english-please-b880a3214a82/&#34;&gt;great explanation&lt;/a&gt;. Understanding how websites communicate with your vendors is very important.&lt;/p&gt;
&lt;p&gt;The point is that if you plan on doing any volume of business, at some point you will need to exchange information with some vendor or other. Verifying that your new website is capable of exchanging information with existing vendors is a big consideration for you to make &lt;em&gt;before&lt;/em&gt; you build. Building a website without the required APIs would be like building a house without any provision for water, plumbing, or electricity. This is important stuff, and it can get very expensive to retrofit!&lt;/p&gt;
&lt;h3 id=&#34;is-that-all&#34;&gt;Is that all?&lt;/h3&gt;
&lt;p&gt;Absolutely not. There are endless considerations that can play into your decision. But hopefully this post has given you some good ideas of where to start and will help you avoid &lt;a href=&#34;https://en.wikipedia.org/wiki/Analysis_paralysis&#34;&gt;analysis paralysis&lt;/a&gt;. Use this information to get a good idea of your needs and document them, so that you can investigate the right things. Compare how well different solutions fit your needs.&lt;/p&gt;
&lt;p&gt;Who knows, you may start with a SaaS solution for a few years, and then when things are rolling and you have a better handle on your site layout, migrate to a custom-built site. There are no set rules.&lt;/p&gt;
&lt;p&gt;Don’t read an ad for a website service offering to find out you what you need in a website. Determine what you need in your website and look for a product which meets your needs!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Why upgrading software libraries is imperative</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/06/why-upgrading-software-libraries-is-imperative/"/>
      <id>https://www.endpointdev.com/blog/2020/06/why-upgrading-software-libraries-is-imperative/</id>
      <published>2020-06-10T00:00:00+00:00</published>
      <author>
        <name>Selvakumar Arumugam</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/06/why-upgrading-software-libraries-is-imperative/image-0.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://unsplash.com/photos/PlBsJ5MybGc&#34;&gt;Image&lt;/a&gt; by &lt;a href=&#34;https://unsplash.com/@toluobde&#34;&gt;Tolu Olubode&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com&#34;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Applications primarily run on front- and back-end programming languages, including library dependencies. Operating systems and programming languages can be periodically updated to run on the latest version, but what about the many libraries being used in the app’s front and backend? As we all know, it can be quite a daunting task to maintain and individually update a long list of software dependencies like the examples later in this post. Still, it is important to keep them updated.&lt;/p&gt;
&lt;p&gt;This post dives into our experience upgrading a complex app with a full software stack and lots of dependencies. We’ll examine the benefits of upgrading, what you will need, and how to go about such an upgrade as simply as possible.&lt;/p&gt;
&lt;p&gt;The app in question contained decade-old software and included extensive libraries when we received it from our client. The app used languages including Java, Scala, Kotlin, and JavaScript along with many libraries. The initial plan was to upgrade the complete software stack and libraries all at once due to the gap between versions. This proved to be more difficult than expected due to a host of deprecated and removed functionality as well as interdependence of a few of the libraries.&lt;/p&gt;
&lt;h3 id=&#34;conflict-approach-dont-update-unless-you-have-to&#34;&gt;Conflict approach: “Don’t update unless you have to”&lt;/h3&gt;
&lt;p&gt;While this can be sustainable in the short term, it quickly becomes less applicable in the long run. One important purpose of updates is to (hopefully) protect from new vulnerabilities and cyber attacks. Scenarios arise where particular library fixes are implemented on the latest version, yet require upgrading other libraries to the latest version in a chain. Because upgraded libraries need extensive testing and preparation for new issues, this directly impacts whether the app attempts to resolve an issue.&lt;/p&gt;
&lt;p&gt;Therefore, smaller and more frequent updates are more sustainable in the long run. Larger and less frequent upgrades will not only result in unexpected errors, but also require more complete regression testing to deliver a bug-free update. The following reasons justify the necessity of software and library upgrades:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They apply timely security patches to reduce vulnerabilities and defend from cyberattack&lt;/li&gt;
&lt;li&gt;You can identify deprecated functionality earlier and use alternatives&lt;/li&gt;
&lt;li&gt;They can apply fixes for known bugs in the library&lt;/li&gt;
&lt;li&gt;They’re based on the latest versions of software or library, encouraging stability&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition, staying on the latest major version yields the benefit of applying minor and patch releases seamlessly without risk. Most software and libraries use semantic versioning, formatted as MAJOR.MINOR.PATCH:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MAJOR version - Incompatible API changes&lt;/li&gt;
&lt;li&gt;MINOR version - Add functionality with backward compatiblity&lt;/li&gt;
&lt;li&gt;PATCH version - Bug fixes with backward compatibility&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So being on the latest major versions of libraries allows applying minor and patch releases without breaking existing functionalities of the app.&lt;/p&gt;
&lt;h3 id=&#34;benefits&#34;&gt;Benefits&lt;/h3&gt;
&lt;p&gt;The benefits of keeping software and libraries updated include bug fixes, new features, boosted performance, as well as better stability, compatibility, and security measures. We can often ignore upgrades in projects because we don’t perceive significant effects in appearance or usage. But on closer inspection, frequent updates deliver important advantages.&lt;/p&gt;
&lt;h3 id=&#34;real-time-difficulties&#34;&gt;Real-time difficulties&lt;/h3&gt;
&lt;p&gt;In real-life scenarios, bug fixes and performance optimization of an app often take a higher priority than upgrading libraries. In many cases, this results in a discrepancy between the app’s version and the current version. This discrepancy can lead to the following issues when upgrading libraries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unexpected error handling in new releases&lt;/li&gt;
&lt;li&gt;Unexpected unsupported library dependencies&lt;/li&gt;
&lt;li&gt;Need for complete end-to-end testing due to major version of library upgrade&lt;/li&gt;
&lt;li&gt;Lack of work being visibly completed can lead to a lack of confidence from client&lt;/li&gt;
&lt;li&gt;Difficulty in estimating workload due to major unexpected errors&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following passages offer guidance in how to adequately prepare for these issues.&lt;/p&gt;
&lt;h3 id=&#34;get-prepared&#34;&gt;Get prepared&lt;/h3&gt;
&lt;p&gt;Much can be learned from preparing for a major upgrade on both a business and technical level. First, a complete list of libraries from the entire app should be compiled. This list should include the latest and current version of the library in the app. Reviewing the changelog of both versions will help identify potential incompatibilities or problems. Visiting the “issue report page” of the library to monitor any version-specific issues is also recommended.&lt;/p&gt;
&lt;p&gt;Once you’ve adequately prepared the libraries for upgrading, you can use any method of your choosing to upgrade and maintain the list. Once the roadmap is established, upgrading can commence and compatibility issues that arise can be dealt with as they arise. Finally, thorough end-to-end testing is necessary once the upgrade process is complete.&lt;/p&gt;
&lt;p&gt;Below are some examples of software lists you might have for a project (we left out the latest versions for brevity in this post, but it’s helpful to have a column for those too!).&lt;/p&gt;
&lt;h5 id=&#34;programming-software--servers&#34;&gt;Programming software &amp;amp; servers&lt;/h5&gt;
&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;Software&lt;/th&gt;&lt;th&gt;Running&lt;/th&gt;&lt;th&gt;Release date&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ubuntu Linux&lt;/td&gt;&lt;td&gt;12.04.5&lt;/td&gt;&lt;td&gt;2017-04-28&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;nginx web server&lt;/td&gt;&lt;td&gt;1.12.2&lt;/td&gt;&lt;td&gt;2017-10-17&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Jetty Java Servlet container&lt;/td&gt;&lt;td&gt;9.2.3&lt;/td&gt;&lt;td&gt;2014-09-05&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;JVM/Java&lt;/td&gt;&lt;td&gt;1.7u79&lt;/td&gt;&lt;td&gt;2015-04-14&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Scala&lt;/td&gt;&lt;td&gt;2.11&lt;/td&gt;&lt;td&gt;2014-04-21&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kotlin&lt;/td&gt;&lt;td&gt;0.9.223&lt;/td&gt;&lt;td&gt;2014-10-23&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Lucene&lt;/td&gt;&lt;td&gt;4.10.0&lt;/td&gt;&lt;td&gt;2014-09-03&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;PostgreSQL database&lt;/td&gt;&lt;td&gt;9.3.10&lt;/td&gt;&lt;td&gt;2015-10-08&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h5 id=&#34;java-libraries&#34;&gt;Java libraries&lt;/h5&gt;
&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;Software&lt;/th&gt;&lt;th&gt;Running&lt;/th&gt;&lt;th&gt;Release date&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;joda-convert&lt;/td&gt;&lt;td&gt;1.3.1&lt;/td&gt;&lt;td&gt;2013-03-01&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;joda-time&lt;/td&gt;&lt;td&gt;2.2&lt;/td&gt;&lt;td&gt;2013-03-01&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;play-json&lt;/td&gt;&lt;td&gt;2.7.0&lt;/td&gt;&lt;td&gt;2019-01&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;httpclient&lt;/td&gt;&lt;td&gt;4.1.2&lt;/td&gt;&lt;td&gt;2011-07-10&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;httpcore&lt;/td&gt;&lt;td&gt;4.1.2&lt;/td&gt;&lt;td&gt;2011-07-22&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;httpmime&lt;/td&gt;&lt;td&gt;4.1.2&lt;/td&gt;&lt;td&gt;2011-07-29&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;commons-codec&lt;/td&gt;&lt;td&gt;1.3&lt;/td&gt;&lt;td&gt;2005-10-01&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;jooq&lt;/td&gt;&lt;td&gt;3.1.0&lt;/td&gt;&lt;td&gt;2013-06-30&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;jooq-codegen&lt;/td&gt;&lt;td&gt;3.1.0&lt;/td&gt;&lt;td&gt;2013-06-30&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;jooq-meta&lt;/td&gt;&lt;td&gt;3.1.0&lt;/td&gt;&lt;td&gt;2013-06-30&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;jtds&lt;/td&gt;&lt;td&gt;1.3.0&lt;/td&gt;&lt;td&gt;2012-10-27&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;servlet-api&lt;/td&gt;&lt;td&gt;3.1&lt;/td&gt;&lt;td&gt;2013-04-25&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;play-json-joda&lt;/td&gt;&lt;td&gt;2.11-2.6.9&lt;/td&gt;&lt;td&gt;2018-03-01&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;lucene-core&lt;/td&gt;&lt;td&gt;4.10.0&lt;/td&gt;&lt;td&gt;2014-09-02&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;lucene-memory&lt;/td&gt;&lt;td&gt;4.10.0&lt;/td&gt;&lt;td&gt;2014-09-02&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;lucene-queries&lt;/td&gt;&lt;td&gt;4.10.0&lt;/td&gt;&lt;td&gt;2014-09-02&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;lucene-queryparser&lt;/td&gt;&lt;td&gt;4.10.0&lt;/td&gt;&lt;td&gt;2014-09-02&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;lucene-highilghter&lt;/td&gt;&lt;td&gt;4.10.0&lt;/td&gt;&lt;td&gt;2014-09-02&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;lucene-analyzers-common&lt;/td&gt;&lt;td&gt;4.10.0&lt;/td&gt;&lt;td&gt;2014-09-02&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;postgresql&lt;/td&gt;&lt;td&gt;9.2-1003-jdbc4&lt;/td&gt;&lt;td&gt;2013-05-27&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;jsoup&lt;/td&gt;&lt;td&gt;1.7.3&lt;/td&gt;&lt;td&gt;2013-11-11&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;jetty-util&lt;/td&gt;&lt;td&gt;9.2.5.v20141112&lt;/td&gt;&lt;td&gt;2014-11-12&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;tika-core&lt;/td&gt;&lt;td&gt;1.16&lt;/td&gt;&lt;td&gt;2017-07-07&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;commons-fileupload&lt;/td&gt;&lt;td&gt;1.3&lt;/td&gt;&lt;td&gt;2013-03-24&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;commons-beanutils&lt;/td&gt;&lt;td&gt;1.6&lt;/td&gt;&lt;td&gt;2005-11-08&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;commons-collections&lt;/td&gt;&lt;td&gt;2.1&lt;/td&gt;&lt;td&gt;2005-11-08&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;commons-digester&lt;/td&gt;&lt;td&gt;2.1&lt;/td&gt;&lt;td&gt;2010-09-24&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;commons-io&lt;/td&gt;&lt;td&gt;1.1&lt;/td&gt;&lt;td&gt;2005-11-24&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;commons-lang&lt;/td&gt;&lt;td&gt;1.0.1&lt;/td&gt;&lt;td&gt;2005-11-24&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;commons-logging&lt;/td&gt;&lt;td&gt;1.0.3&lt;/td&gt;&lt;td&gt;2005-11-17&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;commons-validator&lt;/td&gt;&lt;td&gt;1.0.2&lt;/td&gt;&lt;td&gt;2005-11-24&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;slf4j-api&lt;/td&gt;&lt;td&gt;1.6.4&lt;/td&gt;&lt;td&gt;2011-10-31&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;slf4j-nop&lt;/td&gt;&lt;td&gt;1.6.4&lt;/td&gt;&lt;td&gt;2011-10-31&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h5 id=&#34;javascript-libraries&#34;&gt;JavaScript libraries&lt;/h5&gt;
&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;Software&lt;/th&gt;&lt;th&gt;Running&lt;/th&gt;&lt;th&gt;Release date&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;angular&lt;/td&gt;&lt;td&gt;1.2.11&lt;/td&gt;&lt;td&gt;2014-02-03&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;jQuery&lt;/td&gt;&lt;td&gt;1.10.2&lt;/td&gt;&lt;td&gt;2013-07-03&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;calendar&lt;/td&gt;&lt;td&gt;1.51&lt;/td&gt;&lt;td&gt;2005-03-07&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;calendar-setup&lt;/td&gt;&lt;td&gt;1.25&lt;/td&gt;&lt;td&gt;2005-03-07&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;fckeditor&lt;/td&gt;&lt;td&gt;2.6.11&lt;/td&gt;&lt;td&gt;2014-06-02&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;dom-drag&lt;/td&gt;&lt;td&gt;—&lt;/td&gt;&lt;td&gt;2001-10-28&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;jt_DialogBox&lt;/td&gt;&lt;td&gt;29&lt;/td&gt;&lt;td&gt;2012-08&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;jt_AppDialogs&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;2005-05&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ng-grid&lt;/td&gt;&lt;td&gt;2.0.7&lt;/td&gt;&lt;td&gt;2013-06-07&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ng-grid-flexible-height&lt;/td&gt;&lt;td&gt;2.0.7&lt;/td&gt;&lt;td&gt;2013-06-07&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ng-grid.css&lt;/td&gt;&lt;td&gt;2.0.7&lt;/td&gt;&lt;td&gt;2013-06-07&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&#34;recommendations&#34;&gt;Recommendations&lt;/h3&gt;
&lt;p&gt;Once the app’s software stack and libraries are up to date it’s important to regularly update. The complete upgrade experience can be a rigorous and involved process that includes juggling numerous issues and intensive testing.&lt;/p&gt;
&lt;p&gt;From a client’s perspective, a major upgrade project often doesn’t obviously demonstrate improvement to appearance or function. Because it is not recommended to keep outdated software and major upgrades (such as the one described above) are tedious, the following update options are preferable in order to keep software libraries as up-to-date as possible:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Interval Based:&lt;/em&gt; Particular interval periods are implemented to check versions of software. This minimizes frequency of problems while ensuring a manageable gap between the current and latest software version.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Module Based:&lt;/em&gt; Whenever work is performed on any module for new features or bug fixes, software and library versions are reviewed. This allows for relevant libraries to be updated, tested, and deployed along with development changes within the module.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Even if new features and improvements are not pertinent to the new release, frequent upgrades to software and libraries are crucial in order to ensure the most secure and debugged versions. Please feel free to share your methods of managing software updates in a comment!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>In-House to Consulting, Rinse and Repeat</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/03/in-house-to-consulting/"/>
      <id>https://www.endpointdev.com/blog/2018/03/in-house-to-consulting/</id>
      <published>2018-03-28T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2018/03/in-house-to-consulting/burst-image.jpg&#34; alt=&#34;In House vs. Consulting&#34; /&gt;&lt;br /&gt;
&lt;small&gt;&lt;a href=&#34;https://burst.shopify.com/photos/business-team-meeting-in-boardroom&#34;&gt;Photo by Matthew Henry of Burst&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Hi! I’m Steph. I’ve been working 10+ years now as a software engineer, and I’ve spent ~4 years of that as an in-house developer at 2 different companies and 7+ years as a consultant here at End Point and as an independent contractor. Whenever I’ve started a new position, I get asked “What’s it like on the other side?” or something along the lines of “Is the grass greener?” I thought my answer to this was good blog fodder, so here we are.&lt;/p&gt;
&lt;p&gt;If you google “in-house vs. consultant”, much of the results are written from the perspective of a company looking to hire either in-house engineers or looking to outsource to consulting companies. Here’s my approach to answer the question as it relates to employee happiness, skill development, and job satisfaction.&lt;/p&gt;
&lt;h3 id=&#34;working-as-an-in-house-engineer&#34;&gt;Working as an in-house engineer&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Working as an in-house engineer can mean that you may focus on one technology (or one web stack) and even one main codebase, although this isn’t necessarily true. When I worked for &lt;a href=&#34;https://www.backcountry.com/&#34;&gt;Backcountry.com&lt;/a&gt; a while back, the main application was an ecommerce application running on &lt;a href=&#34;https://www.interchangecommerce.org/&#34;&gt;Interchange&lt;/a&gt;, and that’s where I spent most of my dev time. But as my experience grew, I began working on other internal B2B tools as well.&lt;/li&gt;
&lt;li&gt;With a small team, it’s possible you might adopt consistent style rules in the code.&lt;/li&gt;
&lt;li&gt;In an engineering role as an in-house dev, you might not be directly involved in sales efforts of the company.&lt;/li&gt;
&lt;li&gt;With a small, single team, there is only one set of team dynamics that you have to navigate between, which might allow you to build stronger relationships with those team members and understand how to work with those team members efficiently. Of course the disadvantage of this is if you don’t get along with a team member, you should learn how to work with them.&lt;/li&gt;
&lt;li&gt;Working as an engineer for a product company might mean a steady stream of income and less micromanaging of expenses. This &lt;em&gt;might&lt;/em&gt; translate to benefits like maternity leave, simply because the revenue stream can support paid leave, but this isn’t necessarily true and highly dependent on the circumstances. On the flip side, if the company isn’t doing well financially, there isn’t a resource for additional income, which could translate to loss of jobs for employees.&lt;/li&gt;
&lt;li&gt;From my experience, working with a small team, there have been better processes in place for code review and code feedback, ultimately resulting in better growth of individual engineers. I have found that while I was an in-house engineer, I did a deep dive into one technology stack, and my skills greatly improved with that depth of experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;working-as-a-consultant&#34;&gt;Working as a consultant&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Working as a consultant often offers the ability to be exposed to many technologies, many technology stacks, and more variety.&lt;/li&gt;
&lt;li&gt;From my experience, adaptability is highly valued in the consulting space because you are exposed to different technology stacks and development processes. It is valuable to move seamlessly between teams and technologies.&lt;/li&gt;
&lt;li&gt;Depending on how your company is set up, there can be a wide range of types of client interaction. Some clients want to learn how to make website changes and your role is to support them in that desire. Other clients may be hands-off all dev work, expecting you to have the expertise to drive technology choices. Clients may have very limited budgets and want a consultant to provide the most value to them. Client management is very important here.&lt;/li&gt;
&lt;li&gt;In consulting, from my experience, it is not unusual to participate in sales efforts to provide estimates or technical solutions for proposals.&lt;/li&gt;
&lt;li&gt;While I find many successful software engineering consultants have a breadth of knowledge, companies can often grow talent in specific areas or with specific hires to gain a depth of knowledge in specific technologies. The benefit to other employees is the access to expertise that may translate to better efficiency for clients (for example when I ping other expert co-workers when a question is not in my skillset).&lt;/li&gt;
&lt;li&gt;By nature, consulting is diverse, so job stability may be decent if clients are overall successful and you are successful in working with them.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;working-for-a-company-that-does-both-consulting-and-product-development&#34;&gt;Working for a company that does both consulting and product development&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I’ve seen a few companies that have been successful in both consulting and product development, where their employees develop a product but supplement company income with expert consulting.&lt;/li&gt;
&lt;li&gt;From an employee perspective, this may offer a nice balance between breadth and depth in working on specific technology stacks.&lt;/li&gt;
&lt;li&gt;A company that does both product development and consulting successfully may be more stable financially due to diversified income streams.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;in-house--consulting&#34;&gt;In-house &amp;amp; consulting&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;In both scenarios, I’ve found there is always a feedback loop going on in during active software development. You complete some task, pass it on for review, wait for feedback from a &lt;em&gt;decision maker&lt;/em&gt; to iterate to the next step or adjustments. I like to fill in time when I’m waiting by working on another feature (in-house) or other client work (consulting).&lt;/li&gt;
&lt;li&gt;My personal experience has taught me that communication is one of the most valuable assets to have as a software engineer, because you are responsible for translating business needs to software. The better you can communicate with intent, the more efficient you will be as an in-house engineer and as a consultant.&lt;/li&gt;
&lt;li&gt;The ability to calculate work effort and manage priorities is also a highly valued skill in both employment circumstances.&lt;/li&gt;
&lt;li&gt;From my own experience, I’ve been lucky to receive flexibility in both scenarios working half/​part-time since my first daughter was born. In the consulting space, working limited time may be suitable for limited client budgets, but as an in-house engineer working half-time translated to less output while still providing knowledge from previous experience.&lt;/li&gt;
&lt;li&gt;I’ve also found that in both scenarios, some job satisfaction comes from the happiness of the client (or &lt;em&gt;decision maker&lt;/em&gt;), which can put you in a difficult position if your client isn’t happy. Every time the &lt;em&gt;decision maker&lt;/em&gt; walks away dissatisfied, I think you have an opportunity to improve how to communicate and work with them to build a successful relationship.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;conclusion-is-the-grass-greener-on-the-other-side&#34;&gt;Conclusion: Is the grass greener on the other side?&lt;/h3&gt;
&lt;p&gt;If you ask me “What’s the best option for me?”, I can’t answer that for you in your particular circumstances. I think an in-house position establishes a good foundation in a single set of technologies that could be beneficial as you progress in your career should you choose to climb the ladder in your current position or move to a consulting position. But if you are adaptable, and want to grow your breadth of experience and exposure to technologies, I think consulting is a good fit.&lt;/p&gt;
&lt;p&gt;In true consulting form, I can only suggest to anyone considering a job change from one to another that you evaluate factors such as job satisfaction, compensation, flexibility, and stability when you are evaluating your options.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Reconciling Android source code</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/12/android-reconciling-source/"/>
      <id>https://www.endpointdev.com/blog/2017/12/android-reconciling-source/</id>
      <published>2017-12-19T00:00:00+00:00</published>
      <author>
        <name>Zed Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;Recently, a client came to us with an interesting problem. They needed some changes made to an internal-use Android app that had been created for them by another company, but they didn’t have up-to-date source code for the app, and they had no way of getting it. They gave us an old archive of source code, a more recent working build of the app, and asked us to figure it out.&lt;/p&gt;
&lt;p&gt;The working version of the app they sent us was built in early 2016, and the app was compiled for Android SDK version 22, which is 5.1 Lollipop. It came out in March 2015, so it was a year old even when the app was created, and the code was using lots of features which have since been deprecated.&lt;/p&gt;
&lt;p&gt;After putting this source code in a Git repository, I started by building the app from the source they’d sent and comparing it to the working version. It looked mostly the same, but a couple of features were broken. At the suggestion of a coworker, I used &lt;a href=&#34;https://github.com/Konloch/bytecode-viewer/releases&#34;&gt;BytecodeViewer&lt;/a&gt; to decompile the APK and took a look around.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2017/12/android-reconciling-source/bytecode-viewer.jpg&#34; alt=&#34;BytecodeViewer screenshot&#34;&gt;&lt;/p&gt;
&lt;p&gt;It was a little while before I was able to find any differences between the source we had and this decompiled code, but I did find a few logic differences. The source was also using a few old APIs that needed to be updated. With the needed changes made to the source code, I soon had a working build of the app that matched the functionality of the built APK we had.&lt;/p&gt;
&lt;p&gt;This wasn’t the only thing wrong with the app, however. Another hurdle in picking up this project from its previous developers showed up when I had to fix a few odd problems reported by the users. One problem was on an information screen listing attributes of items. An example:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2017/12/android-reconciling-source/details.jpg&#34; alt=&#34;Details screen&#34;&gt;&lt;/p&gt;
&lt;p&gt;The “Quantity” field worked just fine for any non-zero value, but if it was 0, the app was displaying 16 instead. This turned out to be very simple to fix. Below is the layout code involved:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;TextView&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;android:layout_width=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;wrap_content&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;android:layout_height=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;wrap_content&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;android:textAppearance=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;?android:attr/textAppearanceMedium&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;android:text=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;16&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;android:textColor=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;@color/background_black&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;android:id=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;@+id/txt_info_quantity&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The default text of that field was, likely as a placeholder, set to 16, and it wasn’t being overwritten when the given value was 0. So, all I had to do was replace it with 0, since it would be overwritten anytime there was a nonzero value.&lt;/p&gt;
&lt;p&gt;This was a good reminder to somehow keep track of placeholders like this, for example, putting a todo comment or something similar, so bugs like this can be caught in code reviews.&lt;/p&gt;
&lt;p&gt;Figuring out how to reconcile the source and binary of this app seemed like a daunting task, but it turned out to be very doable, and was a very interesting project.&lt;/p&gt;

      </content>
    </entry>
  
</feed>
