Friday, July 31, 2020

Domain Driven Design Chapter 11 Summary

Chapter 11: Applying Analysis Patterns
    • "Deep models and supple designs don’t come easily. Progress comes from lots of learning about the domain, lots of talking, and lots of trial and error. Sometimes, though, we can get a leg up."
    • "When an experienced developer looking at a domain problem sees a familiar sort of responsibility or a familiar web of relationships, he or she can draw on the memory of how the problem was solved before. [...] Some of these patterns have been documented and shared, allowing the rest of us to draw on the accumulated experience."
    • "They let us cut through expensive trial and error to start with a model that is already expressive and implementable and addresses subtleties that might be costly to learn. From that starting point, we refactor and experiment. These are not out-of-the-box solutions."
    • "Analysis patterns are groups of concepts that represent a common construction in business modeling. It may be relevant to only one domain or it may span many domains.' ~Martin Fowler 
  • Analysis Patterns are Knowledge to Draw On
    • "When you are lucky enough to have an analysis pattern, it hardly ever is the answer to your particular needs. Yet it offers valuable leads in your investigation, and it provides cleanly abstracted vocabulary. It should also give you guidance about implementation consequences that will save you pain down the road."
    • "Sometimes the result doesn’t even obviously relate to the analysis pattern itself, yet was stimulated by the insights from the pattern."
    • "There is one kind of change you should avoid. When you use a term from a well-known analysis pattern, take care to keep the basic concept it designates intact, however much the superficial form might change. There are two reasons for this. First, the pattern may embed understanding that will help you avoid problems. Second, and more important, your ubiquitous language is enhanced when it includes terms that are widely understood or at least well explained. If your model definitions change through the natural evolution of the model, take the trouble to change the names too."
    • "This kind of reapplication of organized knowledge is completely different from attempts to reuse code through frameworks or components, except that either could provide the seed of an idea that is not obvious. A model, even a generalized framework, is a complete working whole, while an analysis is a kit of model fragments. Analysis patterns focus on the most critical and difficult decisions and illuminate alternatives and choices. They anticipate downstream consequences that are expensive if you have to discover them for yourself."

Wednesday, July 15, 2020

Domain Driven Design Chapter 10 Summary

Chapter 10: Supple Design
    • "When software with complex behavior lacks a good design, it becomes hard to refactor or combine elements. Duplication starts to appear as soon as a developer isn’t confident of predicting the full implications of a computation. Duplication is forced when design elements are monolithic, so that the parts cannot be recombined. Classes and methods can be broken down for better reuse, but it gets hard to keep track of what all the little parts do. When software doesn’t have a clean design, developers dread even looking at the existing mess, much less making a change that could aggravate the tangle or break something through an unforeseen dependency. In any but the smallest systems, this fragility places a ceiling on the richness of behavior it is feasible to build. It stops refactoring and iterative refinement.

      To have a project accelerate as development proceeds—rather than get weighed down by its own legacy—demands a design that is a pleasure to work with, inviting to change. A supple design."
    • "There is no formula for designing software like this, but I have culled a set of patterns that, in my experience, tend to lend suppleness to a design when they fit. These patterns and examples should give a feel for what a supple design is like and the kind of thinking that goes into it."
  • Intention-Revealing Interfaces
    • "If a developer must consider the implementation of a component in order to use it, the value of encapsulation is lost. If someone other than the original developer must infer the purpose of an object or operation based on its implementation, that new developer may infer a purpose that the operation or class fulfills only by chance. If that was not the intent, the code may work for the moment, but the conceptual basis of the design will have been corrupted, and the two developers will be working at cross-purposes."
    • "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 ubiquitous language so that team members can quickly infer their meaning. Write a test for a behavior before creating it, to force your thinking into client developer mode."
  • Side-Effect-Free Functions
    • "Interactions of multiple rules or compositions of calculations become extremely difficult to predict. The developer calling an operation must understand its implementation and the implementation of all its delegations in order to anticipate the result. The usefulness of any abstraction of interfaces is limited if the developers are forced to pierce the veil. Without safely predictable abstractions, the developers must limit the combinatory explosion, placing a low ceiling on the richness of behavior that is feasible to build."
    • "Place as much of the logic of the program as possible into functions, operations that return results with no observable side effects. Strictly segregate commands (methods that result in modifications to observable state) into very simple operations that do not return domain information. Further control side effects by moving complex logic into value objects when a concept fitting the responsibility presents itself."
    • "Side-effect-free functions, especially in immutable value objects, allow safe combination of operations. When a function is presented through an intention-revealing interface, a developer can use it without understanding the detail of its implementation."
  • Assertions
    • "When the side effects of operations are only defined implicitly by their implementation, designs with a lot of delegation become a tangle of cause and effect. The only way to understand a program is to trace execution through branching paths. The value of encapsulation is lost. The necessity of tracing concrete execution defeats abstraction."
    • "State post-conditions of operations and invariants of classes and aggregates. If assertions cannot be coded directly in your programming language, write automated unit tests for them. Write them into documentation or diagrams where it fits the style of the project’s development process.

      Seek models with coherent sets of concepts, which lead a developer to infer the intended assertions, accelerating the learning curve and reducing the risk of contradictory code."
  • Conceptual Contours
    • "When elements of a model or design are embedded in a monolithic construct, their functionality gets duplicated. The external interface doesn’t say everything a client might care about. Their meaning is hard to understand, because different concepts are mixed together.

      On the other hand, breaking down classes and methods can pointlessly complicate the client, forcing client objects to understand how tiny pieces fit together. Worse, a concept can be lost completely. Half of a uranium atom is not uranium. And of course, it isn’t just grain size that counts, but just where the grain runs."
    • "The goal is a simple set of interfaces that combine logically to make sensible statements in the ubiquitous language, and without the distraction and maintenance burden of irrelevant options. This is typically an outcome of refactoring: it’s hard to produce up front. But it may never emerge from technically oriented refactoring; it emerges from refactoring toward deeper insight."
  • Standalone Classes
    • "Even within a module, the difficulty of interpreting a design increases wildly as dependencies are added. This adds to mental overload, limiting the design complexity a developer can handle. Implicit concepts contribute to this load even more than explicit references."
    • "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. Every such self-contained class significantly eases the burden of understanding a module."
    • "Dependencies on other classes within the same module are less harmful than those outside. Likewise, when two objects are naturally tightly coupled, multiple operations involving the same pair can actually clarify the nature of the relationship. The goal is not to eliminate all dependencies, but to eliminate all nonessential ones. If every dependency can’t be eliminated, each one that is removed frees the developer to concentrate on the remaining conceptual dependencies."
    • "Try to factor the most intricate computations into standalone classes, perhaps by modeling value objects held by the more connected classes"
    • "Eliminating dependencies should not mean dumbing down the model by arbitrarily reducing everything to primitives."
  • Closure of Operations
    • "Another common practice in refined designs is what I call “closure of operations.” The name comes from that most refined of conceptual systems, mathematics. 1 + 1 = 2. The addition operation is closed under the set of real numbers. Mathematicians are fanatical about not introducing extraneous concepts, and the property of closure provides them a way of defining an operation without involving any other concepts. We are so accustomed to the refinement of mathematics that it can be hard to grasp how powerful its little tricks are. But this one is used extensively in software designs as well. The basic use of XSLT is to transform one XML document into another XML document. This sort of XSLT operation is closed under the set of XML documents. The property of closure tremendously simplifies the interpretation of an operation, and it is easy to think about chaining together or combining closed operations."
    • "Where it fits, define an operation whose return type is the same as the type of its argument(s). If the implementer has state that is used in the computation, then the implementer is effectively an argument of the operation, so the argument(s) and return value should be of the same type as the implementer. Such an operation is closed under the set of instances of that type. A closed operation provides a high-level interface without introducing any dependency on other concepts."
  • Declarative Design
    • "This term means many things to many people, but usually it indicates a way to write a program, or some part of a program, as a kind of executable specification. A very precise description of properties actually controls the software. In its various forms, this could be done through a reflection mechanism or at compile time through code generation (producing conventional code automatically, based on the declaration). This approach allows another developer to take the declaration at face value. It is an absolute guarantee. "
    • Pitfalls to watch out for:
      • "A declaration language not expressive enough to do everything needed, but a framework that makes it very difficult to extend the software beyond the automated portion"
      • "Code-generation techniques that cripple the iterative cycle by merging generated code into handwritten code in a way that makes regeneration very destructive"
    • "The unintended consequence of many attempts at declarative design is the dumbing-down of the model and application, as developers, trapped by the limitations of the framework, enact design triage in order to get something delivered."
    • "The greatest value I’ve seen delivered has been when a narrowly scoped framework automates a particularly tedious and error-prone aspect of the design, such as persistence and object-relational mapping. The best of these unburden developers of drudge work while leaving them complete freedom to design."
    • Domain-Specific Languages
      • "An interesting approach that is sometimes declarative is the domainspecific language. In this style, client code is written in a programming language tailored to a particular model of a particular domain. [...] In such a language, programs can be extremely expressive, and make the strongest connection with the ubiquitous language. This is an exciting concept, but domain-specific languages also have their drawbacks in the approaches I’ve seen based on object-oriented technology."
      • "To refine the model, a developer needs to be able to modify the language. This may involve modifying grammar declarations and other language-interpreting features, as well as modifying underlying class libraries. I’m all in favor of learning advanced technology and design concepts, but we have to soberly assess the skills of a particular team, as well as the likely skills of future maintenance teams."
  • A Declarative Style of Design
    • "Once your design has intention-revealing interfaces, side-effect-free functions, and assertions, you are edging into declarative territory. Many of the benefits of declarative design are obtained once you have combinable elements that communicate their meaning, and have characterized or obvious effects, or no observable effects at all."
    • Extending Specifications in a Declarative Style
      • "Specification is an adaptation of an established formalism, the predicate. Predicates have other useful properties that we can draw on, selectively."
      • Combining Specifications Using Logical Operators
        • "When using specifications, you quickly come across situations in which you would like to combine them. As just mentioned, a specification is an example of a predicate, and predicates can be combined and modified with the operations “AND,” “OR,” and “NOT.” These logical operations are closed under predicates, so specification combinations will exhibit closure of operations."
        • "Also, this full generality is not needed in many cases. In particular, AND tends to be used a lot more than the others, and it also tends to create less implementation complexity. Don’t be afraid to implement only AND, if that is all you need."
      • Subsumption
        • "This final feature is not usually needed and can be difficult to implement, but every now and then it solves a really hard problem. It also elucidates the meaning of a specification."
        • "A more stringent spec subsumes a less stringent one. It could take its place without any previous requirement being neglected."
        • "In the language of SPECIFICATION, we would say that the new SPECIFICATION subsumes the old SPECIFICATION, because any candidate that would satisfy the new SPEC would also satisfy the old.

          If each of these SPECIFICATIONS is viewed as a predicate, subsumption is equivalent to logical implication. Using conventional notation, A➝ B means that statement A implies statement B, so that if A is true, B is also true."
        • "Unfortunately, when OR and NOT are included, these proofs become much more involved. In most situations it is best to avoid such complexity by making a choice, either forgoing some of the operators or forgoing subsumption. If both are needed, consider carefully if the benefit is great enough to justify the difficulty."
  • Angles of Attack
    • "You can’t just look at an enormous system and say, “Let’s make this supple.” You have to choose targets."
    • Carve off Subdomains
      • "You just can’t tackle the whole design at once. Pick away at it. Some aspects of the system will suggest approaches to you, and they can be factored out and worked over. [...] With each such step, not only is the new module clean, but also the part left behind is smaller and clearer. [...] It is more useful to make a big impact on one area, making a part of the design really supple, than to spread your efforts thin."
    • Draw on Established Formalisms, When You Can
      • "Creating a tight conceptual framework from scratch is something you can’t do every day. Sometimes you discover and refine one of these over the course of the life of a project. But you can often use and adapt conceptual systems that are long established in your domain or others, some of which have been refined and distilled over centuries."
      • "There are many such formalized conceptual frameworks, but my personal favorite is math. It is surprising how useful it can be to pull out some twist on basic arithmetic. Many domains include math somewhere. Look for it. Dig it out. Specialized math is clean, combinable by clear rules, and people find it easy to understand."

Friday, July 10, 2020

Domain Driven Design Chapter 9 Summary

Chapter 9: Making Implicit Concepts Explicit
    • "A deep model has power because it contains the central concepts and abstractions that can succinctly and flexibly express essential knowledge of the users’ activities, their problems, and their solutions. The first step is to somehow represent the essential concepts of the domain in the model. Refinement comes later, after successive iterations of knowledge crunching and refactoring. But this process really gets into gear when an important concept is recognized and made explicit in the model and design."
    • "Many 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 then represent it explicitly in the model with one or more objects or relationships."
  • Digging Out Concepts
    • Listen to Language
      • "Listen to the language the domain experts use. Are there terms that succinctly state something complicated? Are they correcting your word choice (perhaps diplomatically)? Do the puzzled looks on their faces go away when you use a particular phrase? These are hints of a concept that might benefit the model."
      • "This is not the old “nouns are objects” notion. Hearing a new word produces a lead, which you follow up with conversation and knowledge crunching, with the goal of carving out a clean, useful concept."
    • Scrutinize Awkwardness
      • "The concept you need is not always floating on the surface, emerging in conversation or documents. You may have to dig and invent. The place to dig is the most awkward part of your design. The place where procedures are doing complicated things that are hard to explain. The place where every new requirement seems to add complexity."
      • "Now you have to actively engage the domain experts in the search. If you are lucky, they may enjoy playing with ideas and experimenting with the model. If you are not that lucky, you and your fellow developers will have to come up with the ideas, using the domain expert as a validator, watching for discomfort or recognition on his or her face."
    • Contemplate Contradictions
      • "Different domain experts see things different ways based on their experience and needs. Even the same person provides information that is logically inconsistent after careful analysis. Such pesky contradictions, which we encounter all the time when digging into program requirements, can be great clues to deeper models."
    • Read the Book
      • "Different domain experts see things different ways based on their experience and needs. Even the same person provides information that is logically inconsistent after careful analysis. Such pesky contradictions, which we encounter all the time when digging into program requirements, can be great clues to deeper models."
    • Try, Try Again
      • "The examples I’ve given don’t convey the amount of trial and error involved. I might follow half a dozen leads in conversation before finding one that seems clear and useful enough to try out in the model. I’ll end up replacing that one at least once later, as additional experience and knowledge crunching serve up better ideas."
      • "All these changes of direction are not just thrashing. Each change embeds deeper insight into the model."
  • How to Model Less Obvious Kinds of Concepts
    • "Things, event very abstract ones [...], are the meat of most object models, along with the actions those things take. [...] But other important categories of concepts can be made explicit in a model as well."
    • Explicit Constraints
      • "Constraints make up a particularly important category of model concepts. They often emerge implicitly, and expressing them explicitly can greatly improve a design."
      • "[W]arning signs that a constraint is distorting the design of its host object.
        • "Evaluating a constraint requires data that does not otherwise fit the object’s definition."
        • "Related rules appear in multiple objects, forcing duplication or inheritance between objects that are not otherwise a family"
        • "A lot of design and requirements conversation revolves around the constraints, but in the implementation, they are hidden away in procedural code."
    • Processes as Domain Objects
      • "What I am talking about here are processes that exist in the domain, which we have to represent in the model. When these emerge, they tend to make for awkward object designs."
      • "The key to distinguishing a process that ought to be made explicit from one that should be hidden is simple: Is this something the domain experts talk about, or is it just part of the mechanism of the computer program?"
    • 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 predicate-like value objects for specialized purposes. A specification is a predicate that determines if an object does or does not satisfy some criteria"
    • Applying and Implementing Specification
      • "Much of the value of SPECIFICATION is that it unifies application functionality that may seem quite different. We might need to specify the state of an object for one or more of these three purposes. "
        • Validation - "To validate an object to see if it fulfills some need or is ready for some purpose"
          • Given an object, return whether or not the specification is satisfied by the object.
        • Selection (Querying) - "To select an object from a collection (as in the case of querying for overdue invoices)"
          • While this could be applied at the code level using the same code used for specification, there are a number a situations where this would prove impractical, and instead approaches of specifying the specification so that it can be applied at a sql query level may be necessary, and various approaches for this are discussed.
        • Building to Order (Generating) - "To specify the creation of a new object to fit some need"
          • You can create a generator that takes in a specification to satisfy, and the generator will return an object that satisfies the specification.
          • "Building to order can mean creation of an object from scratch, but it can also be a configuration of preexisting objects to satisfy the spec."