Software Architecture Books Summary And Highlights -- Part 8 Service Design
Highlights
What make a good service?
loose coupling and high cohesion
MSV Ch 3 How to model service
Integration Guidelines
service change shouldn’t break consumer
Keep Your APIs Technology-Agnostic
Make Your Service Simple for Consumers: providing a client lib makes client easy to use but increase coupling
Hide Internal Implementation Detail
MSV Ch 4 Integration
Integrability
It is about how to integrate a dependency into a system and its difficulty.
type of services distances
- Syntactic distance: data type difference e.g. int vs float
- data semantic distance: data meaning difference e.g. integer means latitude or altitude
- behavioral semantic distance: e.g. states and modes of the system.
- temporal distance: e.g. rates, order of events, latency
- resource distance: assumptions about shared resources, devices, memories
Tactics:
- Limiting dependency:
- provide a simple interface to externals
- Use an Intermediary : e.g. pub-sub bus
- Restrict Communication Paths
- Adhere to Standards
- Abstract Common Services. e.g. one interface, different implementation
- adapt
- discovery: applications and services discover each other
- Tailor Interface capabilities in an existing interface without changing the API or implementation.
- make component configurable during build time, or runtime
- Coordinate
- orchestration: reduce dependency by centralizing them
- manage resource: software can only request resource such as thread or memory from a resource manager and cannot access them directly
SAP Ch 7 Integrability
3 main dimensions for service coupling:
- Communication: sync or async (event based)
- Consistency: atomic or eventual consistency
- Coordination: choreography or orchestration
SAP Ch 2 Discerning Coupling in Software Architecture
Orchestration & choreography
Orchestration
semantic coupling: coupling caused by business requirements
implementation coupling: only caused by implementation
The major lesson of the last decade of architecture design is to model the semantics of the workflow as closely as possible with the implementation.
Choreography
3 patterns:
- the Front Controller pattern places the responsibility for state on the first called service in the chain of responsibility
- keep no transient workflow state at all, relying on querying the individual services to build a real-time snapshot.
- stamp coupling, storing extra workflow state in the message contract sent between services. Each domain service updates its part of the overall state and passes that to the next in the chain of responsibility.
Orchestration vs Choreography
The sweet spot for choreography lies with workflows that need responsiveness and scalability, and either don’t have complex error scenarios or they are infrequent.
On the other hand, orchestration is best suited for complex workflows that include boundary and error conditions. While this style doesn’t provide as much scale as choreography, it greatly reduces complexity in most cases.
SAH Ch 11 Managing Distributed Workflows
In general stamp coupling is discouraged in distributed architectures. Because
- Over-specifying details in contracts is generally an anti-pattern
- the famous fallacies of distributed computing: bandwidth is infinite
But choreography workflow with stamp coupling is an attractive one because different microservices in the chain can pass the whole big object around and query it easily
SAH Ch 13 Contracts
Async communication
Asynchronicity isn’t a simple change—it adds many layers of complexity to architecture, especially around coordination, requiring much more complexity in the mediator.
Asynchronous communication, while offering better responsiveness, makes resolving timing and synchronization issues difficult—race conditions, deadlocks, queue reliability, and a host of other distributed architecture headaches reside in this space.
SAH Ch 12 Transactional Sagas
Consumer-driven service contracts
a pull model;
the consumer puts together a contract for the items they need from the provider, and passes the contract to the provider, who includes it in their build and keeps the contract test green at all times. The contract encapsulates the information the consumer needs from the provider.
SAH Ch 13 Contracts
Service Granularity monitoring metrics:
- number of statements
- number of interfaces
When to decompose service?
- Service scope and function
- Is the service doing too many unrelated things?
- Based on cohesion and size of components
- Code volatility
- Are changes isolated to only one part of the service?
- Isolate frequently changed code into separate services
- Scalability and throughput
- Do parts of the service need to scale differently? if yes e.g. with different qps, then better to split
- Fault tolerance
- Are there errors that cause critical functions to fail within the service?
- Whenever breaking apart a service, regardless of the disintegration driver, always check to see if strong cohesion can be formed with the “leftover” functionality.
- Security
- Do some parts of the service need higher security levels than others?
- break into separate services and enforce more secure way to access sensitive data
- Extensibility
- Is the service always expanding to add new contexts?
- After adding additional features, it is easier to test a smaller service
- Our advice is to apply this driver only if it is known ahead of time that additional consolidated contextual functionality is planned, desired, or part of the normal domain.
When to merge service
- Database transactions Is an ACID transaction required between separate services?
- Workflow and choreography
- Do services need to talk to one another?
- Too many communication among services may worsen
- fault tolerance (cascading failure);
- performance
- overall data integrity and reliability
- Shared code: Do services need to share code among one another?
- Need to consider following things:
- Specific shared domain functionality
- Frequent shared code changes
- if shared code is changed frequently, better to bundle all callers into one service
- Defects that cannot be versioned
- If a shared lib has defects, better to bundle all callers together so that the fix can be applied quicker and easier
- Need to consider following things:
- Data relationships: Although a service can be broken apart, can the data it uses be broken apart as well? some data may be needed for reading and writing by all services
SAH Ch 7 Service Granularity
Technically, it should be possible to create well-factored, independent modules within a single monolithic process. And yet we rarely see this happen. The modules themselves soon become tightly coupled with the rest of the code, surrendering one of their key benefits.
MSV Ch1 What is MicroService
How to keep model consistent
Bounded context
The model context is whatever set of conditions must apply in order to be able to say that the terms in a model have a specific meaning.
- 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 codebases and database schemas
- Keep the model strictly consistent within these bounds , but don’t be distracted or confused by issues outside.
CONTINUOUS INTEGRATION means that all work within the context is being merged and made consistent frequently enough that when splinters happen they are caught and corrected quickly.
Continuous integration cost could be very high when
- functionality integration is limited
- the teams do not have the skill or the political organization to maintain continuous integration
So model separation might be a solution in such case.
patterns for separate models
- shared kernel: 2 teams share some subset of the domain model
- SHARED KERNEL is difficult in a case where different implementation technologies are being used
- supplier/consumer : Upstream and downstream subsystems separate naturally into two BOUNDED CONTEXTS.
- CUSTOMER/SUPPLIER TEAMS are more likely to succeed if the two teams work under the same management, so that ultimately they do share goals
- Customer/Supplier Development Teams pattern may not work when the supplier team has no incentive to cooperate
- conformist: completely follow the supplier’s model and share the UBIQUITOUS language and only extend it.
- anti corruption layer: an isolating layer translates in both directions as necessary between the two models .
- separate ways a BOUNDED CONTEXT with no connection to the others at all, allowing developers to find simple, specialized solutions within this small scope.
Context Map
Caveats when mapping between different contexts
- Code reuse between BOUNDED CONTEXTS is a hazard to be avoided.
- Integration of functionality and data must go through a translation.
- Describe the points of contact between the models , outlining explicit translation for any communication and highlighting any sharing.
Large bounded context vs Small bounded context
large bounded context | small bounded context | |
---|---|---|
Consistency within bounded context | Larger contexts may call for more versatile abstract models, requiring skills that are in short supply. | easier with smaller teams and code bases. |
Whole system integration | Flow between user tasks is smoother when more is handled with a unified model. | Translation between two models can be difficult (sometimes impossible). |
Communication | Shared language fosters clear team communication. | Communication overhead between developers is reduced. |
Development | It is easier to understand one coherent model than two distinct ones plus mappings. | Different models can cater to special needs or encompass the jargon of specialized groups of users, along with specialized dialects of the UBIQUITOUS LANGUAGE. |
DDD ch14 Maintaining Model Integrity
Bounded Context
Any given domain consists of multiple bounded contexts, and residing within each are things that do not need to be communicated outside as well as things that are shared externally with other bounded contexts. Each bounded context has an explicit interface, where it decides what models to share with other contexts.
Prematurely decomposing a system into microservices can be costly, especially if you are new to the domain. In many ways, having an existing codebase you want to decompose into microservices is much easier than trying to go to microservices from the beginning.
When you start to think about the bounded contexts that exist in your organization, you should be thinking not in terms of data that is shared, but about the capabilities those contexts provide the rest of the domain.
MSV Ch 3 How to model service
Refactoring
How to refactor domain model
- core domain
- Boil the model down. Find the CORE DOMAIN and provide a means of easily distinguishing it from the mass of supporting model and code.
- Make the CORE small.
- generic subdomain
- some subdomains are general principles everyone knows or details that belong to specialties which are not your primary focus but play a supporting role.
- Factor out these subdomains and place them in separate MODULES.
- You can either buy an existing solution or outsource the implementation for these generic subdomain
- domain vision statement
- Write a short description (about one page) of the CORE DOMAIN and the value it will bring
- It gives the team a shared direction.
- highlighted core
- Write a very brief document ( three to seven sparse pages ) that describes the CORE DOMAIN and the primary interactions among CORE elements .
- Flag each element of the CORE DOMAIN in code base and Make it effortless for a developer to know what is in or out of the CORE.
- cohesive mechanism
- Extract cohesive and complicated computation and algorithm implementation into a separate lightweight framework
- segregated core
- separate the CORE concepts from supporting players
- strengthen the cohesion of the CORE while reducing its coupling to other code.
- Segregating the CORE will sometimes make relationships with tightly coupled non-CORE classes more obscure or even more complicated, but that cost is outweighed by the benefit of clarifying the CORE DOMAIN and making it much easier to work on.
- abstract core
- Identify the most fundamental concepts in the model and factor them into distinct classes , abstract classes , or interfaces .
- Start with refactoring core domain.
DDD ch 15 distillation
Related Chapters
DDD ch14 Maintaining Model Integrity
DDD ch 15 distillation
SAP Ch 7 Integrability **
SAP Ch 2 Discerning Coupling in Software Architecture
SAH Ch 7 Service Granularity
SAH Ch 11 Managing Distributed Workflows
SAH Ch 12 Transactional Sagas
SAH Ch 13 Contracts
MSV Ch 1 What is MicroService
MSV Ch 3 How to model service
MSV Ch 4 Integration
MSV Ch 5 Splitting the monolith