Clean Code & Architecture Course
📏

Clean Code & Architecture Course

Created
Jul 24, 2022 02:16 PM
Language
English
Summary
Course by Rodrigo Branas on Clean Code, Clean Architecture, DDD, Design Patterns and CQRS.
Attention Status
Ignorable
You don't know if code is working, until the users are using it.
We are not omniscient beings. Unit tests make sure that units of our system work as intended. But we don't know all scenarios a unit will have to handle beforehand. End-to-end and integration tests allow us to discover which behaviors some units need to handle, that we didn't previously know.
"Design is inevitable, the alternative to good design is bad design, not no design at all." This also applies to any important part of software development. When you neglect the organization of a domain, you will have a problem.
 

Date: ‣

Topic: Events, Design Patterns, Event Sourcing, CQRS

Recall

  • What is SAGA pattern?
  • Does the concept of segregating UX rules from business rules makes sense?
  • The Event Sourcing pattern is great to keep track of how a state came to be.
  • Events can be used without event sourcing. Just as CQRS can.

Notes

  • To generate and to publish and event, are not the same thing. Events may be generated anywhere, be it in the domain or application layers. But domain should not be responsible for publishing events.
  • Eventual consistency is acceptable is most cases. Only consider stronger consistency when your use case requires so.
  • Queues are common in the world. They exist because its impossible to keep the maximum available resources at all times.
  • Events are commands, and they are dealt by command handlers.
  • You don’t need to always use an external queue for events. Depending on your use case, you can use memory queues. Design patterns like Mediators may help.
  • Distributed systems are way more complex than monolithic ones.
  • Some states are computed from a series of events. To ensure consistency, and have the ability of recovering to a certain state, we can store those events. That is the pattern Event Sourcing.
  • We can use a mediator to keep track of the event stream, while updating the state of some other object.
  • Since event sourcing leaves you to handle not a state, but a series of events, the amount of data increases rapidly. A good practice to lessen that burden is to make snapshots that consolidate events into meaningful information. (E.g. having a monthly process that calculates the balance of an account.)
  • The domain model handles business logic and the read model handles queries on data consolidated by an event bus [visual 1].
  • The Command design pattern solves the problem of decoupling who calls, from who is called. An instance that is a command, carries all data necessary to execute its logic, at any given time. That means when you actually want to execute it, you should not have to pass any additional information.
  • The Observer pattern allows us to create relationships between components, in which one just says it wants to know when the other changes. Once the observable one changes, it notifies every one observing it about the new state.
  • The Mediator pattern allows us to create a broadcast feature. When a Mediated sends a message through the Mediator, it then broadcasts to every other Mediated listening.

Visuals

1
1
📌
SUMMARY: Some use cases are better expressed as a series of events (e.g. account balance, quantity on stock). For that you can use event sourcing.
 

 

Date: ‣

Topic: SOLID, CQRS

Recall

  • When you see a switch, it can be an indicator that you can use a Design Pattern.
  • If there is a piece of code that may be changed, without inflicting changes in other parts of the class it is in, consider O/C principle.
  • If you implement an interface, but don’t need some methods it has, ISP.
  • Repositories should be used for orchestration of business rules. For complex reads, use a different model.

Notes

  • Single Responsibility means cohesion. If things have different reasons to change, they should be segregated. For example, a Freight object which needs the volume of package, should not be responsible for calculating it. Volume is not specific to a freight.
  • The simplest and hardest principle to get around is the Single Responsibility. Itself is what we do with software. Define boundaries over concepts in a way they integrate and evolve well enough.
  • A good practical way to measure cohesion is checking if the methods in a class uses most of its instance properties.
  • Coupling by itself is not a bad thing. It gets bad when coupling different concepts. When one changes, the other, which shouldn’t, ends up being changed or breaks for no reason.
  • Open/Closed principle states that classes should not change to add new ways to do some behavior. They should change only when the behavior itself changes. (It is confuse.) I.e. for a behavior calculateTotalPrice(items) this method, should not know how to calculate the price of a single item. It should only do item.calculatePrice().
  • (Barbara) Liskov’s Substitution principle states that classes which extend another one, should adhere to the common behavior of the extended class. Meaning that an algorithm that uses class A's method print(), should work when that instance is B extends A or C extends A, or any other extension of A.
  • By Liskov’s Substitution principle, sub-classes should accept at least the same the super-class accepts. Never less than it. E.g. a method from the super-class accepts integers, but the sub-class denies negative integers. If that happens, the sub-class does not belong to the same family of classes.
  • On Liskov’s Substitution principle: preconditions cannot be strengthened in the subtype; postconditions cannot be weakened in the subtype; invariants must be preserved in the subtype.
  • Interface Segregation principle says that interfaces should define only methods they must have. Otherwise, use different interfaces. (I.e. a class implementing an interface shouldn’t have non-implemented methods.)
  • High-level modules, should not depend on low-level ones. Use abstractions and the Dependency Inversion principle to solve that.
  • CQRS promotes the segregation of how you write and read data.
  • When you design your software around business rules, you end up with hard-to-do projections of data. Repositories are great for resolving data used by domain logic. But some data projections, used for reasons outside the domain, are extremely hard on business oriented repositories.
📌
SUMMARY: SOLID states abstract principles on how to keep a domain clean. CQRS promotes the separation of the model of read and write.
 

 

Date: Jul 24, 2022

Topic: DDD, Ports and Adapters, Design Patterns, CQRS

Recall

  • Ports and Adapters define two types of ports: driving and driven
  • You can use Domain Services to keep a business rule in the domain layer.
  • The application layer is naturally procedural; the domain shouldn’t.
  • Systems requirements may have different nature, CQRS addresses how to handle that.
  • Repositories just recover or change state. They shouldn’t make invariant validation.
  • Some times its good to break encapsulation.
  • There are some patterns to integrate bounded-contexts.
  • Use anti-corruption layers to avoid weakening your domain.

Notes

  • In Ports and Adapters (P&A) ports are interfaces and the adapters are the implementations of those interfaces.
  • There are two types of ports in P&A: driving (input) they execute flows of the application; driven (output) they are used by the application. DDD has their own version of this: Infrastructure Services.
  • The Input Boundary of our system are the driving ports.
  • Examples of driving ports are Controllers, UI, CLI, Queue Consumer; and of driven ports, are Database, Queue Producer, File System, other services’ APIs.
  • P&A just tell how we can avoid coupling using interfaces for things that are external to our application or domain.
  • When you cannot fit a business rule into an Aggregate or Value Object, you can use a Domain Service to define it where it belongs, the domain layer and then use it on the orchestrator (Use Case/Application Service/…).
  • CQRS is Command and Query Responsibility Segregation. It means that commands/business rules should be segregated from queries.
  • Why CQRS? Some requirements of an application do not fit in a business UseCase. For example, to generate a report file, you need a lot of data, commonly distant from each other. To avoid destroying your domain aggregates, you shouldn’t use them to query data for a report.
  • A repository is just responsible for saving or retrieving the state of objects. They don’t need to pass through invariant validation. They are just recovering the state of an already valid object.
  • Some times its valid to break encapsulation. For example, when recovering state of an object. Actually, there is even a pattern for it: memento.
  • You can integrate bounded-contexts with: Shared Kernel, a piece shared between multiple bounded-contexts; Customer/Supplier, a supplier defines an API which the customer uses; Conformist, you just accept and use an already defined API (the supplier will not change it for you); Separate-ways, just don’t integrate.
  • When integrating bounded-contexts there will be differences in their domain. Use an anti-corruption layer (ACL), to adapt it and avoid weakening them.
  • A good approach for an ACL is to have one for every external bounded-context you integrate your domain with.
  • The Abstract Factory pattern is used to define how to create a family of similar objects (e.g. a RepositoryFactory that creates the repositories of your application)
📌
SUMMARY: P&A has driving and driven ports. Which are interfaces implemented with Adapters. CQRS states that you don’t need to go a bad route, just because that route is good for something else. Create different routes for each piece.
Â