Friday, September 28, 2018

Implementing Domain-Driven Design (Book notes)

2018-implementing-ddd

Notes from Vaughn Vernon book Implementing Domain-Driven Design

Note that I have read the "Blue Book" (Eric Evans' Domain-Driven Design) previously, so mainly I haven't repeated things from that book.

Foreword and Preface

Foreword by Eric Evans

  • Nine years after my book (...) was published, there's actually a lot to say about DDD that is new, and there are new ways to talk about the fundamentals.

Preface

  • Sometimes DDD is first embraced as a technical tool set (referred to as DDD-Lite by some).
  • If there is a single invention Evans delivers to the software development community, it is the Ubiquitous language.

Chapter 1: Getting Started with DDD

  • DDD isn't first and foremost about technologies. In its most central principles, it's about discussion, listening, understanding, discovery and business value, all in an effort to centralize knowledge.
  • If you're capable of understanding the business ..., you can at a minimum participate on discovery process for a Ubiquitous Language.
  • How DDD helps?
    • 1: DDD brings domain experts and software developers together in order to develop software that reflects the mental model of the business experts
      • Instead of "most realistic", thrive to deliver a model that is most useful for the business.
    • 2: DDD addresses the strategic initiatives of the business
    • 3: DDD has tactical design modeling tools to analyze and develop software
  • Use DDD to simplify, not to complicate!
  • Business value for DDD
    • The organizations gains a useful model of its domain
    • A refined, precise definition and understanding of the business is developed
    • ...

Chapter 2: Domains, Subdomains, and Bounded Contexts

These things are "Strategic design".

  • In broad sense, a domain is what an organization does and the world it does it in.
  • Bounded Context is chiefly a linguistic boundary.
    • Some projects fall into the trap of attempting to create an all-inclusive model. (Which is a pitfall)
    • Same term might have very different meaning in different bounded contexts.
    • Bounded Context often marks off a system, an application or a business service.
    • With persistence, a database schema will live inside the boundary.
    • How big should it be? A bounded context should be as big as it needs to be in order to fully express it's complete Ubiquitous Language.
    • Often a single team for a single bounded context works pretty well

Division between different parts of the business domain:

  • Core Domain is a part that is of primary importance to the success of the organization.
    • Supporting Subdomain: Bounded Context that models some aspect of the business that is essential, yet not Core.
    • Generic Subdomain: Subdomain that captures nothing special to the business, yet is required for the overall business solution.

Problem space vs solution space. (This chapter deals mainly on problem space assessment)

Chapter 3: Context Maps

This chapter focuses on the solution space assessment.

  • Context map shows the mappings between Bounded Contexts.
  • By drawing a Context Map, you will be forced to think about your relationships with all other projects / Contexts you depend on.
  • A Context Map should capture the existing terrain, not the imagined future. First focus on the current situation and where you are. After that determine where to go next.
  • (Going through the patterns in Evans' "Strategic Design" part)
  • We might have state dependent from another system.
    • Instead of caching whole dependent objects, we create local domain objects translated from the foreign model.

Chapter 4: Architecture

  • The goal is to use just the right choices and combinations of architecture and architecture patterns.

Layers

Hexagonal / Ports and Adapters

  • See Alistair Cockburn's Hexagonal Architecture article.
  • Ports and Adapters name also used.
  • Instead of "Front end" and "Back end" Hexagonal looks are "Inside" and "Outside"
    • Domain Model furthest inside.
  • Normally we don't implement the ports ourselves. (Most of that comes from a framework, container etc.)
  • For remainder of the chapter, assume that Ports and Adapters approach is used.

Service-Oriented

REST

  • Two alternatives for combining DDD and RESTful HTTP
    • 1: Create a separate Bounded Context for the system's interface layer
    • 2: Reflect domain objects in resources
      • As a problem, any changes to objects structure are immediately reflected in remote interfaces.

CQRS

From Wikipedia:

... every method should either be a command that performs an action, or a query that returns data to the caller, but not both. In other words, asking a question should not change the answer. More formally, methods should return a value only if they are referentially transparent and hence possess no side effects.

Examining Areas of CQRS (see overview image)

  • Query Processor takes queries from client.
  • Query Model (aka Read Model)
  • Command Processors
  • Command Model (aka Write Model)
  • Event Subscriber updates the Query Model
    • Synchronous or asynchronous? Depends on the normal load of the system, where the query model DB is stored, data consistency constraints and performance requirements.
    • If Query model is Eventually Consistent, it needs to be considered in the UI. Different approaches:
      • As a trick, show the data UI in the UI as the command would already be successfully executed.
      • Show date and time from the query model that the user is currently viewing.

Summa summarum, CQRS introduces a number of competing forces

Event-Driven Architecture

  • Pipes and Filters
  • Long-Running processes (aka Sagas)
    • Problem of knowing which process was ending etc?
      • One way is to assign a unique Process identity.
      • Store an aggregate-like state object tracking process state & completion.

Event Sourcing

  • Sometimes the business cares about tracking changes that occur to the objects in the domain model.
  • -> Store history of everything that's happened.
  • To avoid playback of many events, we can apply optimization with (Aggregate) state snapshots.

Data Fabric and Grid-Based Distributed Computing

Chapter 5: Entities

  • Developers have tendency to focus on data rather than the domain.
    • One reason being many approaches placing importance on the database.
    • Instead of designing domain concepts with rich behavior, we might think primarily about the attributes
  • Domain concept is designed as an Entity when we care about its individuality.
    • Distinguishing it from all other objects in a system is a mandatory constraint.
  • Some strategies for identity creation:
    • User provides one or more unique values as input to the application.
    • Application internally generates an identity using some algorithm that ensures uniqueness
    • The application relies on a persistence store, e.g. database.
    • Unique identity is already determined by another Bounded Context.
  • Identity generation timing can be done either
    • Early (identity is generated before the Entity is persisted. As part of object's construction.
    • Late (identity is generated when the Entity is persisted). As part of object's persistence.
  • Surrogate identity
    • Some tools (e.g. ORMs) want to deal with Object identity on their own terms.
    • -> Two identities:
      • One designed for the domain model
      • One for the tool, known as a surrogate identity
    • It's best to hide the surrogate attribute from the outside world.
  • Entity validation - can be done at three levels
    • Validating single attributes/properties (not null, certain values, certain format, ...)
      • Referred to as defensive programming.
    • Validating Whole Objects
      • Validations that need to have access to the state of the entire object.
      • Specification/Strategy pattern might be used.
    • Validating Object Compositions
      • Often at Aggregate level.

Chapter 6: Value Objects

  • Evans: When you care only about the attributes of an element of the model, classify it as a Value Object.
    • Treat the Value Object is immutable.
    • Don't give it any identity.
  • We should thrive to model using Value Objects instead of Entities whenever possible.
  • Make sure you address the Ubiquitous Language.
  • Value objects usually possess most of these characteristics:
    • It measures, quantifies or describes a thing in the domain.
    • It can be maintained as immutable.
    • It models a conceptual whole.
      • Parent reference to a Value Object is not just an attribute. Rather, it is a property of the containing parent object/thing...
    • It is completely replaceable.
    • It can be compared with others using Value equality.
    • It supplies its collaborators with Side-Effect-Free Behavior.
  • Standard Types Expressed as Values.
    • E.g. Java enums can be used as State object
  • Vernon prefers naming valuePercentage() over getValuePercentage()
    • getValuePercentage() is a technical statement
    • valuePercentage() is a fluent human-readable language expression.
  • If at all possible, design your data model for the sake of your domain model, not vice versa.

ORM things discussed:

  • ORM and Single Value Objects
    • Often Value Object is denormalized into its parent Entity's DB row.
  • ORM and Many values serialized into a single column
    • e.g. List/Set
    • Potential drawbacks to consider
      • Column width
      • Querying - Individual value elements not queryable.
      • Requires custom user type
  • ORM and Many values backed by a database entity
  • ORM and Many values backed by a join table
    • Downsides
      • join needed
      • nulls not supported
      • value type mapped may itself not contain a collection
    • Generally to be avoided
  • ORM and Enum-as-State Objects
    • Requires custom user type.

Chapter 7: Services

  • A Service in the domain is a stateless operation that fulfills a domain-specific task.
    • Often the best indication for service being needed is when the operation you need to perform doesn't fit either to an Aggregate of a Value Object.
  • Don't confuse a Domain Service with an Application Service.
    • We don't want to house business logic in an Application Service.
    • Application Service would normally be a client of a Domain Service.
    • Transactions and security should be addressed as application concerns in Application Services, not in Domain services.
  • When would an operation not belong on an Entity of Value Object? You can use a Domain Service to
    • Perform a significant business process.
    • Transform a domain object from one composition to another.
    • Calculate a Value requiring input from more than one domain object.
  • Make sure you need a service.
  • Is Separated Interface necessary?
    • May be a more a matter of style in cases where the service is always domain specific and will not have technical implementation or multiple implementations.

Chapter 8: Domain Events

  • Contemporary definition for Domain Events: Something happened that domain experts care about.
    • Although domain experts might not initially be aware of the need for every kind of Event, they should understand the reasons for them, as they are included in discussions.
  • When Events are delivered to interesting parties (either local of foreign systems), they are generally used to facilitate eventual consistency
    • This is purposeful and by design.
    • Can eliminate the need for two-phase commits & support of the rules of Aggregates.
  • Does every Aggregate command result in an Event?
    • It is important also to know when to disregard happenings in the domain that experts or the business, as a whole don't care about.
    • Because of technical implementation aspects it is possible that Event will be more prolific than domain experts care about.
  • Modeling Events
    • Name Events and their properties according to the Ubiquitous Language.
    • Typical naming
      • Command operation: BacklogItem#commitTo(Sprint sprint)
      • Event outcome: BacklogItemCommitted
    • At times Events are created by direct request from clients and don't fit a single Aggregate.
      • When that happens, the Event can be modeled as an Aggregate and retained in its own Repository.
    • Identity
      • At times it may be necessary to distinguish Events on from another, but the need may be rare.
      • Unique identity may be necessary when Events are published outside the local Bounded Context, forwarded by messaging infrastructure.
  • Publishing Events
    • Publisher
      • Typical use of Domain Events is when an Aggregate creates an Event and publishes it.
      • The publisher resides in a Module of the model, but it doesn't model an aspect of the domain.
    • Subscribers
      • Generally Application Services subscribe to Domain Events. Sometimes Domain Services.
      • One thing the subscriber should not do is get another Aggregate instance and execute modifying command on it.
      • This would violate the modify-single-aggregate-instance-in-single-transaction rule of thumb.
    • Events are domain-wide concept, not just a concept in a single Bounded Context
      • Events are published to any number of Bounded Contexts of other Subdomains.
  • Spreading the News to Remote Bounded Contexts
    • Some form of messaging takes place.
    • Commitment to Eventual consistency is needed
      • The changes in one model that influence one or more other models will not be fully consistent for some period of time.
    • (At least) two mechanisms must be consistent with each other:
      • Persistence store for the domain model
      • Persistence store backing the messaging infrastructure
    • Autonomous Services and Systems
      • A high degree of independency from other systems is achieved by avoiding in-band RPCs.
      • Use asynchronous messaging to achieve a greater degree of independence between systems - autonomy.
    • Latency Tolerances
      • It may surprise developers to learn that most times, several seconds, minutes, hours or even days between consistent states might be completely tolerable.
      • Not true for all cases.
      • We must not assume that near-consistent time frames would be always imperative for any given domain.
  • Event Store
    • Consider what you could to if you were to store a discrete event for every model command:
        1. Use the Event Store as a queue for publishing all Domain Events through a messaging infrastructure.
        1. Use the same Event Store to feed REST-based Event notifications to polling clients
        • Logically the same as point 1, but different in actual us.
        1. Examine a historical record of the result of every command ever executed on the model.
        1. Use the data in business analytics (trending, forecasting etc.)
        1. Use the Events to reconstitute each Aggregate instance when it is retrieved from its Repository.
        1. Undo blocks of changes to an Aggregate
  • Architectural Styles for Forwarding Stored Events
    • Publishing Notifications as RESTful Resources
      • (+) If potentially many clients can go to a single well-known URI for the same set of notifications, the RESTful approach works well.
      • (-) If one or few consumers are required to pull from multiple producers for resources in order to get a single set of tasks to performed in a specific sequence, the RESTful approach won't probably work too well.
    • Publishing Notifications through Messaging Middleware
  • Event De-Duplication
    • One way to deal with the possibility of duplicate message delivery is for subscriber model operation to be idempotent.
    • When idempotency is not a viable option, you can instead design the subscriber/receiver itself to be idempotent.
      • Messaging product might support this off-the-shelf.

Chapter 9: Modules

  • In a DDD context, Modules in your model serve as named containers for domain classes that are highly cohesive with one another.
  • Simple Do/Don't Rules for Module Design
    • Do design Modules to fit modeling concepts.
    • Do name Modules per the Ubiquitous Language.
    • Don't create Modules mechanically according to a general component type or pattern being used.
    • Do design loosely coupled Modules.
    • Do strive for acyclic dependencies on peer Modules when coupling is necessary.
    • Do relax the rules a bit between child and parent Modules.
    • Don't make Modules as a static concept of the model, but allow them to be molded along with the objects that they organize.
  • Module naming conventions
    • One approach is to divide model and services, e.g.
      • com.foobar.identityaccess.domain.model
      • com.foobar.identityaccess.domain.service
      • com.foobar.collabaration.domain.model
      • com.foobar.collabaration.domain.service
      • Fits well with that we're designing and implementing a model of a domain, not domain.
    • Another approach is to drop model/service division
      • com.foobar.collabaration.domain.conceptname
  • Modules in other layers
    • Same structure can be followed at other layers
      • RESTful resources e.g. com.foobar.collabaration.resources, com.foobar.collabaration.resources.view
      • Application layer modules, e.g. com.foobar.agilepm.application.team

Chapter 10: Aggregates

  • As a start: What is an Aggregate? ... What is this concept of invariants and a consistency boundary all about?
    • The last question is an especially relevant one.
  • Rule: Model True Invariants in Consistency Boundaries
    • An invariant is a business rule that must always be consistent.
    • Different kinds of consistency: Transactional consistency and eventual consistency.
      • When discussing invariants, we're referring to transactional consistency.
    • Consistency boundary logically asserts that everything inside adheres to a specific set of business invariant rules.
    • With a typical persistence solution, we use a single transaction to manage consistency.
    • A properly designed Aggregate is one that can be modified in any way required by the business with its invariants completely consistent within a single transaction.
    • A properly designed Bounded Context modifies only one Aggregate instance per transaction in all cases.
    • -> We can't correctly reason on Aggregate design without applying transactional analysis.
    • Aggregates are chiefly about consistency boundaries and not driven by a desire to design object graphs.
    • Note that these rules are rules of thumb.
  • Rule: Design Small Aggregates
    • With big aggregates, multiple large collections etc. might be loaded during many simple operations.
    • What does "small" mean? The correct minimum is however many are necessary, and no more. :)
      • Often target for just the Root Entity and a minimal number of attributes and/or Value-typed properties.
    • Don't Trust Every Use Case
      • A new use case may lead to insights that push us to remodel the Aggregate.
      • Be skeptical here, too.
      • A large-cluster Aggregate might be problematic
      • Often, in such cases, the business goal can be achieved with eventual consistency between Aggregates.
  • Rule: Reference Other Aggregates by Identity
  • Rule: Use Eventual Consistency Outside the Boundary
    • If executing a command on one Aggregate instance requires that additional business rules execute on one or more other Aggregates, use eventual consistency.
    • "Ask Whose Job It Is" principle:
      • When examining the use case, ask whether it's the job of the user executing the use case to make the data consistent.
        • If it is, try to make it transactionally consistent.
        • If it is another user's job, or the job of the system, allow it to be eventually consistent.
  • Reasons to break the rules
    • 1: User Interface Convenience
    • 2: Lack of Technical Mechanisms
      • user-aggregate affinity: Are the business workflows such that only one user would be focused on one set of Aggregate instances at any given time?
    • 3: Global transaction
    • 4: Query performance
  • Implementation
    • Root Entity with Unique Identity
    • Law of Demeter: Any given method on any object may invoke methods only on the following:
      • Itself
      • Any parameters passed to it
      • Any object it instantiates
      • Self-contained part objects that it can directly access

Chapter 11: Factories

  • A Factory may or may not have additional responsibilities in the domain model other than object creation.
  • Two main options:
    • Factory on Aggregates
      • Factory Methods on Aggregates allow you to express the Ubiquitous Language in ways not possible through constructors alone.
    • Factory on Service

Chapter 12: Repositories

Evans:

For each type of object that requires global access, create an object that can provide an illusion of an in-memory collection of all objects of that type. ... Provide methods to add and remove objects, which will encapsulate the actual insertion or removal of data in the data store. ... Provide Repositories only for Aggregate roots

  • Repositories are all about persistency.
  • Generally, each persistent Aggregate type will have a Repository
  • There is also option of using persistence mechanism's Session or Unit of Work as such. Generally to be avoided.
  • Two kinds of Repository design: collection-oriented design and persistence-oriented design
  • Collection-Oriented Repositories
    • Mimic a collection.
    • A repository interface that does not hint that there is an underlying persistence mechanism, avoiding any notion of saving/persisting data to a store.
    • For a Java in-memory collection, there is no need to do anything special to get the collection to recognize the changes to the objects it contains.
    • Take-aways:
      • A repository should mimic a Set interface.
      • You must not allow instances of the same object to be added twice.
      • When retrieving objects from a Repository and modifying them, you don't need to "re-save" them to the Repository.
    • The persistence mechanism must in some way support tracking changes. Can be done in various ways, including the following:
      • Implicit Copy-on-Read - Each persistent object is copied when it is loaded from the data store.
      • Implicit Copy-on-Write - Proxy objects that make a copy of the managed object when required.
    • Note that in general it is possible that instances of some Aggregate types must never be removed through normal application use cases.
      • Instances might be needed after they are no longer usable in the application. For e.g. referential and/or historical purposes.
      • In this kind of cases Aggregate instances can be marked e.g. as disabled, unusable, or in some other way logically removed.
    • Repository interface is often in domain.
      • Implementation can be placed in infrastructure layer.
      • Another approach is to put implementation in a submodule under Aggregate and Repository module
    • Exceptions: Since we're going to abstract away the implementation details in general, we want to isolate clients from those also with exceptions.
    • TopLInk has both Session and Unit of Work.
      • With Unit of Work, objects to be changed are explicitly registered to the Unit of Work. -> More efficient use of memory & processing power, not so transparent abstraction
  • Persistence-Oriented Repositories
    • "Save-based" approach.
    • The case when the persistence mechanism doesn't implicitly or explicitly detect and track changes.
    • Every time you create a new Aggregate instance or change a pre-existing one, you have to put it explicitly into the data store by using e.g. save.
    • Repository operation both when aggregates are created and when they are modified. (In comparison with collection-oriented repository no explicit operation when existing aggregate is updated)
    • Take-aways:
      • We must explicitly put() both new and changed objects into the store, replacing value previously associated with the given key.
      • These data stores are sometimes called Aggregate Stores or Aggregate-Oriented Databases.
  • Additional behavior
    • Sometimes it is beneficial to provide additional behavior on a Repository interface.
    • E.g. calculations that must be performed in the data store in order to meet some non-functional requirement.
    • At times it may be advantageous to query Aggregate parts out of the Repository without directly accessing the Root itself.
      • This should be used primarily to address performance concerns.
      • Use with caution
      • Special finder methods
      • use case optimal query (often resulting as a Value object)
    • If there's need for many finder methods supporting use case optional queries on multiple Repositories, it's probably a code smell
      • Code smell named Repository masks Aggregate mis-design
  • Transactions
    • Domain layer is never the correct place to manage transactions. Usually transactions should be managed in the Application Layer.
    • Repository implementations should have access to the same Session / Unit of Work for the transaction the Application Layers started.
    • Be careful not to overuse the ability to commit modifications to multiple Aggregates in a single transaction. (If need be, revisit Aggregates)
  • Repository vs Data Access Object (DAO)
    • DAO is expressed in terms of database tables, providing CRUD operations
  • Testing repositories: Two ways to look at testing Repositories
    • Repositories themselves have to be tested.
    • Code using Repositories has to be tested.

Chapter 13: Integrating Bounded Contexts

  • There are always multiple Bounded Contexts in any project of significance, and there will be need to integrate.
  • A few reasonably straightforward ways to integrate Bounded Contexts
    • Expose an API in another Bounded Context and use that API via RPCs from another.
    • Use of messaging mechanism
    • RESTful HTTP
  • Distributed systems are fundamentally different. Author's Principles of Distributed Computing
    • The network is not reliable.
    • There is always some latency, and maybe a lot.
    • Bandwidth is not infinite.
    • Do not assume that the network is secure.
    • Network topology changes.
    • Knowledge and policies are spread across multiple administrators.
    • Network transport has cost.
    • The network is heterogeneous.
  • Experience with
    • Open Host Service: Enhance and expand the protocol to handle new integration requirements
    • -> Provide only what integrators need at present, based on a range of use case scenarios.
  • If at all possible, it is best to minimize or even completely eliminate information duplication across Bounded Contexts.
    • It may not be possible to do that entirely.
    • E.g. SLAs may make it impractical to retrieve remote data every time it is needed.
    • Having the goal to reduce the amount of foreign information will make our jobs much easier.

Chapter 14: Application

  • A domain model often lives at the heart of an /application/.

  • Here we're using the term /application/ somewhat interchangeably with /system/ and /business service/.

    • System is usually a solution with many applications.
  • UI

    • How do we render domain objects onto the glass?
      • There's controversy and disagreement on how best to render objects of the domain model onto the user interface.
      • The UI often benefits from views of data richer than is required to accomplish the direct task.
      • UI often needs to render properties of multiple Aggregate instances.
      • In most use cases, the user performs a state-mutating task that is applied to just one Aggregate instance.
    • DTO approach
      • Design a DTO to hold the entire number of attributes that are needed to display the view.
      • DTO pattern was originally designed to deal with a remote presentation tier that consumes the DTO instances.
    • Mediator to publish aggregate internal state
      • To reduce coupling between the model and it's (UI) clients
      • e.g. BacklogItem.provideBacklogItemInterest(BacklogItemInterest interest) that would call interest.informTenantId(...) etc.
    • Domain Payload Object (DPO)
      • A variant of DTO within a single virtual machine.
      • Contains a reference to whole Aggregate instances.
    • State Representations of Aggregate instances
      • REST-based resources
      • It's good to think a set of RESTful resources as a separate model (View Model / Presentational Model) to avoid coupling clients straight to the domain model.
    • Use Case Optimal Repository Queries
      • Creating a DTO/PTO straight with Repository.
      • This approach has similar motivations than CQRS. (Might be worth going all the CQRS route)
    • Dealing with Multiple Disparate Clients
      • You may design your Application Services to accept Data Transformer
    • Rendition Adapters and Handling User Edits
      • In whatever way your domain data is provided from Application Services - through DTOs, DPOs, or state representations - and whatever presentation framework you use, you may be able to benefit from Presentational Model.
  • Application Services

    • Application Services are direct clients of the domain model. (Options on the logical location were introduced earlier in Architecture chapter)
    • When using an ACID database, the Application Services also control transactions.
    • Security is also commonly cared for by Application Services.
    • Example
      • Some types from the domain model are used in Application Service method signatures.
      • -> UI needs to be aware of these types and depend on them.
      • Alternatively, signature with only primitive types, DTOs and/or Command objects could be used.
      • Spring @Transactional used
      • Potentially also security concerns.
  • Infrastructure

    • To provide technical capabilities for other parts of the application.

Appendix: Aggregates and Event Sourcing: A+ES

Note:

Actual content:

  • Basics
    • Event Sourcing can be used to represent the entire state of an Aggregate as a sequence of Events that have occurred since it was created.
    • State of the Aggregate can be rebuilt from the events.
  • Example flow of operations:
      1. Client calls Application Service
      1. Loads Aggregate state from Event Stream
      1. Call Aggregate method passing parameters got, along with Domain Services as needed
      1. Commit published Events as a Unit of Work to the Event Store to persist the state of the aggregate
      1. New Events are published from Event Store to all subscribers
  • Command Handlers
    • Control the task management of our application.
    • Command Handler effectively replaces the Application Service method.
    • Decoupling the client from the Service can /enhance load balancing, enable competing consumers, and support system partitioning./
    • This approach creates /temporal decoupling/ between clients and the Application Service.
  • Performance
    • Some patterns to apply:
      • Cache Event Streams in server memory (as Events are immutable once written to the Event Stream)
      • Use /snapshots/ of Aggregate instances to avoid loading and replaying big amounts of an Event Stream.
    • With A+ES Aggregates, Aggregates can be partitioned among multiple processes or machines by Aggregate Identity.
  • A+ES aggregates tend to be smaller (following Aggregate Rules of Thumb)
    • Because creating new Aggregates tends to be easier with A+ES than traditional aggregate persistence.
  • Read Model Projections
    • One of the common concerts of A+ES: How to query the Aggregates by their properties?
    • Here Read Model Projections can help
      • Domain Event subscribers that project Events to a persistent Read Model.
      • See sample lokad-cqrs
  • Event enrichers
    • One of A+ES problems comes from event's dual purpose.
      • i) Aggregate persistence
      • ii) Communicate domain-level happenings around the enterprise.
    • Subscribers (ii) often need additional information.
    • We can simplify this by enriching the domain events with additional data.
      • Not essential for reconstructing the aggregate but simplifies Event subscribers

Wednesday, January 10, 2018

Notes from the book Domain-Driven Design

Notes from Eric Evans book Domain-Driven Design - Tackling Complexity in the Heart of Software.

Pattern names should be in bold, when introduced first time.

Foreword & Preface

  • The premise of the book:
    • For most software projects, the primary focus should be on the domain and domain logic.
    • Complex domain designs should be based on a model.

Part I - Putting the Domain Model to Work

  • Example: Map
    • Maps are models, and every model represents some aspect of reality or an idea that is of interest.
    • A model is a simplification.
    • It is an interpretation of reality that
      • abstracts the aspects relevant to solving the problem at hand
      • ignores extraneous detail
  • Domain modeling
    • is not a matter of making as "realistic" model as possible.
    • It is more like moviemaking, loosely representating reality to a particular purpose.

Chapter 1 - Crunching knowledge

  • Knowledge crunching
  • Continuous learning

Chapter 2 - Communication and the Use of the Language

  • Ubiquitous Language - Use the model as the backbone of a language
    • Commit the team to exercising that language relentlessly in all communication within the team and in the code.
  • Model is not the diagram
    • The diagram's purpose is to help communicate and explain the model.
    • Code has the details

Chapter 3 - Binding Model and Implementation

  • Model-Driven Design - Design a portion of the software system to reflect the domain model in a very literal way.
    • Revisit the model and modify it to be implemented more naturally in software, even as you seek to make it reflect deepre insight into the domain.
    • Demand a single model that servers both purposes well, in addition to support a robust Ubuquitous Language.
  • Hands-On Modelers - Any technical person contributing to the model must spend some time touching the code

Part II - The Building Blocks of a Model-Driven Design

Chapter 4 - Isolating the Domain

  • Layered Architecture
  • The Smart UI "Anti-Pattern"
    • If an unsophisticated team with a simple projects decides to try a Model-Driven Design with Layered Architecture, it will face a difficult learning curve.

Chapter 5 - A Model expressed in Software

  • Entity vs Value Object
    • Does an object represent something with continuity and identity - something that is tracked through different states or even across different implementations?
    • Or is it an attribute that describes the state of something else?
  • Service
    • There are those aspects of the domain that are more clearly expressed as actions or operations, rather than objects.
  • Associations
    • For each traversable association in the model, there is a mechanism in the software with the same properties.
    • It is important to constrain relationships as much as possible.
  • Entities (a.k.k Reference Objects)
    • Many objects are not fundamentally defined by their attributes, but rather by a thread of continuity and identity.
    • Sometimes an object must be matched with another object even if their attributes differ.
    • And an object must be distinquished from other objects even though they might have the same attributes.
  • Value Objects
    • Many objects have no conceptual identity.
    • These objects describe some characteristics of a thing.
  • Services
    • Some concepts from the domain aren't natural to model as objects. Forcing them to be resposibility of an Entity or a Value either distorts the definition of a model-based objects or adds meaningless artificial objects.
    • Characteristics of a good service:
      • The opration relates to a domain concept that is not a natural part of an Entity or Value Object
      • The interface is defined in terms of other elemnts of the domain model.
      • The operation is stateless.
  • Modules (a.k.a packages)
    • Everyone uses Modules, but few treat them as a full-fledged part of the model

Chapter 6 - The Life Cycle of a Domain Object

  • Aggregates
    • First we need an abstraction for encapsulating references within the model.
    • Aggregate is a cluster of associated objects that we treat as a unit for the purpose of data changes.
    • Aggregate has a root (one entity) and a ary.
    • Cluster the entities and Value objects into Aggregates and define boundaries around each.
  • Factories
    • When creation of an object, or an entire Aggregate, becomes complicated or reveals too much of the internal structure, Factories provide encapsulation.
    • Creation of an object can be a major operation in itself, but complex assemble operations do not fit the responsibility of the created object.
    • ->
      • Shift the responsibility for creating complex objects and Aggregates to a separate object, which may itself have no responsibility in the domain model but is still a part of the domain design.
      • Create entires Aggregates as a piece, enforcing their invariants.
    • Factory can also be a factory method.
  • Repositories
    • A subset of persistent objects must be globally accessible through a search based on object attributes.
    • Such access is needed for the roots of Aggregates that are not convenient to reach by traversal
    • Usually Entities, sometimes VAlue objects with complex internal structure, and sometimes enumerated Values.
    • ->
      • For each type of object that requires global access, create an object that can provide an illusion of an in-memory collection of all objects of that type.
      • Provide methods to add and remove objects, which will encapsulate the actual insertion or removal of data in the data store.
      • Provide Repositories only for Aggregate roots

Chapter 7 - Using the Language - an extended Example

  • Designing associations: Avoid bi-directional associations

Part III - Refactoring toward Deeper Insight

  • Success developing useful models comes down to three points
    • Sophisticated domain models are achievable and worth the effort
    • They are seldom developed except through an iterative process of refactoring, including close involvement of the domain experts with developers interested in learning about the domain.
    • They may call for sophisticated design skills to implement and to use effectively.
  • A deep model provides a lucid expression of the primary concern of the domain experts and their most relevant knowledge while it sloughs off the superficial aspects of the domain.

Chapter 8 - Breakthrough

Chapter 9 - Making Implicit Concepts Explicit

  • May transformations of domain models and the corresponding code happen when developers recognize a concept that has been hinted at in discussion or present implicitly in the design, and they represent in explicitly in the model with one or more objects or relationships.
  • Experimentation is the way to learn what works and doesn't.
    • Trying to avoid missteps in design will result in a lower quality result because it will be based on less experience.
    • And it can easily take longer than a series of quick experiments.
  • Constraints and processes are two broad categories of model concepts that don't come leaping to mind when programming in an object-oriented language, yet they can really sharpen up a design once we start thinking about them as model elements.
  • Specification
    • Business rules often do not fit the responsibility of any of the obvious Entities or Value Objects, and their variety and combinations can overwhelm the basic meaning of the domain object. But moving the rules out of the domain layer is even worse, since the domain code no longer expresses the model.
    • -> Create explicit predicatelike Value objects for specialized purposes. A Specification is a predicate that determines if an object does or does not satisfy some criteria.
    • Need to specify the state of an objects usually for one or more of three purposes:
      • To validate an object to see if it fulfills some need or is ready for some purpose.
      • To select an object from a collection
      • To specify the creation of a new object to fit some need.

Chapter 10 - Supple Design

  • Intention-revealing Interfaces
    • Name classes and operations to describe their effect and purpose, without reference to the means by which they do what they promise. This relieves the client developer of the need to understand the internals.
    • These names should conform to the Uquiquitous Language.
  • Side-Effect-Free Function
  • Assertions
  • Conceptual Contours
    • (contour: ääriviiva, korkeuskäyrä)
    • Decompose design elements (operations, interfaces, classes and Aggregates) into cohesive units, taking into concideration your intuition of the important divisions in the domain.
    • Observe the axes of change and stability through successive refactorings and look for the underlying Conceptual Contours that explain these shearing patterns.
    • Align the model with the consistent aspects of the domain that make it a viable area of knowledge in the first place.
    • It may never emerge from technically oriented refactoring: It emerges from refactoring toward deeper insight.
  • Stand-Alone Classes
    • Low coupling is fundamental to object design. When you can, go all the way. Eliminate all other concepts from the picture. Then the class will be completely self-contained and can be studied and understood alone.
  • Closure of Operation
    • Where it fits, define an operation whose return type is the same as the type of its argument(s).
    • Such an operation is closed under the set of instances of that type.
  • Angles of attack
    • Carve off subdomains
      • You can't tackle the whole design at once. Pick away at it.
      • It is more useful to make a big impact on one area, making a part of the design very suppple, than to spread your efforts thin.
    • Draw of established formalisms, when you can
      • Creating a right conceptual framework from scratch is something you can't do every day.
      • You can often use and adapt conceptua systems that are long established in your domain or others.

Chapter 11 - Applying Analysis Patterns

Chapter 12 - Relating Design Patterns to the Model

  • Discussion on applying Design Patterns with DDD.
  • Some, not all, of the patterns in Design Patterns can be used as domain patterns.

Chapter 13 - Refactoring Toward Deeper Insight

  • Multifaceted process
  • Three things to focus on:
    • Live in the domain.
    • Keep looking at things a different way.
    • Maintain an unbroken dialog with domain experts.
  • Continuous refactoring has come to be considered a "best practise", but most project teams are still too cautious about it.
  • Refactor when
    • The design does not express the team's current understanding of the domain,
    • Important concepts are implicit in the design (and you see a way to make them explicit), or
    • You see an opportunity to make some important part of the design suppler.

Part IV - Strategic Design

  • As systems grow too complext to know completely at the level of individual objects, we need techniques for manipulating and comprehending large modules.
  • This part of the book presents principles that enable the modeling process to scale up to very complicated domains.

Chapter 14 - Maintaining Model Integrity

  • The most fundamental requirement of a model is that it be internally consistent
    • that its terms always have the same meaning
    • that it contain no contradictory rules
  • Total unification of the domain model for a large system will not be feasible or cost-effective.

  • Bounded Context

    • Multiple models are in play on any large project.
      • Yet when code based on distinct models is combined, software becomes buggy, unreliable and difficult to understand.
      • Communication among team members becomes confused.
      • It is unclear in what context a model should not be applied.
    • -> Explicitly define the context within which a model applies.
      • Explicitly set boundaries in terms of team organization, usage within specific parts of the application, and physical manifestations such as code bases and database schemas.
      • Keep the model strictly consistent within these bounds, but don't be distracted or confused by issues outside.
    • Combining elements of distinct models causes two categories of problems:
      • Duplicate concepts: there are two model elements that actually represent the same concept.
      • False cognates: Case when two people who are using the same term (or implemented object) think they are talking about the same thing, but really are not. (slightly less common, but more insidiously harmful)
  • Continuous Integration

    • All work within the context is being merged and made consistent frequently enough that when splinters happen they are caught and corrected quickly.
  • Context Map

    • Identify each model in play on the project and define its Bounded Context.
    • Name each Bounded Context and make the names part of the Ubiquitous Language.
    • Map the currently existing terrain. Take up transformations later.

Different patterns/approaches for the relationships between Bounded Contexts:

  • Shared Kernel

    • Designate some subset of the domain model that multiple teams agree to share.
    • Including the subset of code or database design associated with that part of the model.
    • This explicitly shared stuff has special status, and shouldn't be changed without consultation with the other team.
  • Customer/Supplier Development Team

    • Establish a clear customer/supplier relationship between the two teams.
    • In planning sessions, make the downstream team play the customer role to the upstream team.
  • Conformistm

    • There might be Upstream/Downstream case in which the upstream has no motivation to provide for the downstream team's needs. Three possible paths:
      • Abandon the use of upstream altogether. (see Separate Ways below)
      • Developing an own model in the downstream team (translation layer will probably be complex - see Anticorruption Layer below)
      • Conformist strategy
    • -> Eliminate the complexity between translation between Bounded Contexts by slavishly adhering to the model of the Upstream team.
  • Anticorruption Layer

    • In translation, data types etc. must be translated. But often overlooked is the certainty that the other system does not use the same conceptual domain model.
    • -> Create an isolating layer to provide clients with functionality in terms of their own domain model. The layer talks to the other system through its existing interface, requiring little or not modification to the other system. Internally, the layer translates in both directions as necessary between the two models.
  • Separate Ways

    • Declare a Bounded Context to have no connection to the other(s) at all, allowing developers to find simple, specialized solutions within this small scope.
  • Open Host Service

    • Define a protocol that gives access to your subsystem as a set of Services. Open the protocol so that all who need to integrate with you can use it. Enhance and expand the protocol to handle new integration requirements, except when a single team has idiosyncratic needs. Then, use a one-off translator to augment the protocol for that special case so that the shared protocol can stay simple and coherent.
  • Published Language

    • Use a well-documented shared language that can express the necessary domain information as a common medium of communication, translating as necessary into and out of that language.

Chapter 15 - Distillation

  • Process of separating the components of a mixture to extract the essence in a form that makes it more valuable and useful.
  • A model is a distillation of knowledge.
  • With every refactoring to deeper insight, we abstract some crucial aspect of domain knowledge and priorities.

  • Core Domain

    • Not all parts of the domain are going to be equally refined.
    • To make the domain model an asset, the model's critical core has to be sleek and fully leveraged to create application functionality. But scarce, highly skilled developers tend to gravitate to technical infrastructure or neatly definable domain problems that can be understood without specialized domain knowledge.
    • Find the Core Domain and provide a means of distinquishing it from the mass of supporting model and core. Bring the most valueable and specialized concepts into sharp relief. Make the Core small.
    • Apply top talent to the Core Domain.
  • Generic Subdomains

    • ->
      • Identify cohesive subdomains that are not the motivation for your project.
      • Factor out generic modules of these subdomains and place them in separate Modules.
      • Leave no trace of your specialties in them.
    • Note: These can be found as off-the-self solutions
    • NOTE: We technical people tend to enjoy definable problems like time zone conversion, and we can easily justify spending our time on them. But a disciplined look at priorities usually points to the Core Domain.
    • Projects face risk from both sides, with some projects having greater technical risks and others greater domain modeling risks. It is easy to underestimate the domain modeling risk.
  • Domain Vision Statement

    • Write a short description (~one page) of the Core Domain and the value it will bring, "value proposition"
    • Ignore those aspects that do not distinguish this domain model from others.
    • Show how the domain model serves and balances diverse interests.
    • Keep it narrow.
    • Write this statement early and revise it as you gain new insight.
  • Highlighted Core

    • -> Write a very brief document (3-7 pages) that describes the Core Domain and the primary interactions among Core elements.
    • -> (another form) Flag each element of the Core Domain within the primary repository in the model, without particularly trying to elucidate its role. Make it effortless for a developer to know what is in or out of the Core.
    • If the distillation document outlines the essentials of the Core domain, then it serves as a practical indicator of the significance of a model change. (If a change affects the distillation document, it requires consultation with the other team memeber)
  • Cohesive mechanisms

    • Partition a conceptually Cohesive Mechanism into a separate lightweight framework. Particularly watch for formalisms or well-documented categories of algorightms. Expose the capabilities of the framework with an Intention-Revealing Interface.
    • For example, graph traversal network would be a cohesive mechanism.
    • Generic Subdomain vs Cohesive Mechanism
      • Both are motivated by desire to unburden the Core Domain
      • Generic Subdomain is based on an expressive model that represents some aspect of how the team views the domain
      • A Cohesive Mechanism does not represent the domain; it solves some sticky computational problem posed by the expressive models.
  • Abstract Core

    • Identify the most fundamental concepts in the model and factor them into distinct classes, or interfaces.
    • Design this abstract model so that it expresses most of the interaction between significant components.
    • Place this abstract overall model in its own Module. (Detailed implementations are left in their own Modules defined by subdomain)

Chapter 16 - Large-Scale Structure

  • Devise a pattern of rules or roles and relationships that will span the entire system and that allows some understanding of each part's place in the whole - even without detailed knowledge of the part's responsibility.

  • Evolving Order

    • Design free-for-alls produce systems no one can make sense of as a whole, and they are very difficult to maintain. But architectures can straitjacket a project with up-front design assumptions and take too much power away from the developers/designers of a particular part of the application.
    • -> Let this conceptual large-scale structure evolve with the application, possibly changing to a completely different type of structure along the way. Don't overconstrain the detailed design and model decisions that must be made with detailed knowledge.
  • System Metaphor
    • Software designs tend to be very abstract and hard to grasp. Developers and users alike need tangible ways to understand the system and share a view of the system as a whole.
    • -> When a concrete analogy to the system emerges that captures the imagination of team members and seems to lead thinking i a useful direction, adopt it as a large-scale structure.
      • Organize the design around this metaphor and absorb it into the Ubiquitous language.
      • Because all metaphors are inact, continually re-examine the metaphor for overextension or inaptness, and be ready to drop it if it gets in the way.
  • Responsibility Layers

    • Look at the conceptual dependencies in your model and the varying rates and sources of change of different parts in the domain. If you identify natural strata in the domain, cast them as broad abstract responsibilities. These responsibilities should tell a story of the high-level purpose and design of your system.
    • Choosing appropriate layers:
      • Some characteristics to look for and preseve:
        • Storytelling
        • Conceptual dependency
        • Conceptual contours
    • Example layering scheme
      • Potential
        • What can be done / what could we do? (Never mind what we are planning to do)
        • Resources, contracts
        • Reality of the situation
      • Operational
        • What is being done?
        • What have we managed to do out of those potentials?
        • Reality of the situation
      • Policy
        • What are the rules and goals?
        • Rules and goals are mostly passive but constrain the behaviour in other layers.
      • Decision Support
        • What action should be taken or what policy should be set?
        • Analysis and decision support
  • Knowledge Level

    • Create a distinct set of objects that can be used to describe and constrain the structure and behavior of the basic model. Keep these concerns separate as two "levels", one very concrete and the other reflecting roles and knowledge that a user or superuser is able to customize.
    • Example of HR system with Employee types etc.
  • Pluggable Component Framework

    • You have to fight the temptation to build frameworks and regiment the implementation of the large-scale structure. The most important contribution of the large-scale structure is conceptual coherence, and giving insight into the domain.
    • Each structural rule should make development easier.

Chapter 17 - Bringing the Strategy Together

  • Three basic principles of strategic design (context, distillation, and large-scale structure) are not subsitues for each other, they are complementary and interact in many ways.
    • E.g. a large-scale structure can exist within one Bounded Context, or it can cut across many of them.
  • Six essentials for Strategic Design Decision Making
    • Decisions must reach the entire team.
    • The decision process must absorb feedback.
    • The plan must allow for evolution.
    • Architecture teams must not siphon off all the best and brightest.
    • Strategic design requires minimalism and humility.
    • Objects are specialists, developers are generalists.

Some links