Part II — Architecture and Methods · Chapter 9

From DDD to Microservices: Decomposing the Monolith

~55 min read4 interactive widgets5 plates

In this chapter

  1. Escaping monolithic hell
  2. Microservices as decomposition into domain services
  3. Microservices and DDD: bounded contexts and granularity
  4. Drivers for modularity and the change scope
  5. Defining the architecture: a three-step process
  6. Step 1 — identifying system operations
  7. Step 2 — decompose by business capability
  8. Step 2 — decompose by subdomain, and away from god classes
  9. Obstacles to decomposition
  10. Step 3 — defining service APIs
  11. Interaction styles and IPC technologies
  12. Designing and evolving service APIs
  13. The missing aspect: concurrency models
  14. Check your understanding

1. Escaping monolithic hell

The module opens with a cautionary tale: FTGO, a leading online food-delivery company from Richardson’s Microservices Patterns [MP]. Consumers use the FTGO website or mobile application to place food orders at local restaurants; FTGO coordinates a network of couriers, pays couriers and restaurants, and lets restaurants edit menus and manage orders. The application uses various web services: Stripe for payments, Twilio for messaging, Amazon SES for email.

Like many aging enterprise applications, FTGO is a monolith — a single Java Web Application (WAR) file — and it has become an example of the Big Ball of Mud pattern: a “haphazardly structured, sprawling, sloppy, duct-tape and bailing-wire, spaghetti code jungle.” The pace of software delivery has slowed, and the application exhibits all the symptoms of monolithic hell.

What the monolith was good at

In the early days, when the application was small, the monolithic architecture had plenty of benefits:

But successful applications have a habit of outgrowing the monolithic architecture: each sprint the code base grows, and the development team grows with it. The once small application becomes a monstrous monolith — and the small team becomes multiple Scrum teams working on the same code base.

THE SEVEN PAIN POINTS OF MONOLITH HELL FTGO monolith single WAR file · one architecture quantum 1 · too complex to understand 2 · slow development 3 · long path to deployment 4 · difficult scaling 5 · unreliable delivery 6 · no fault isolation 7 · technology lock-in complexity spirals: harder to understand → worse changes → more complexity FTGO = the running example of this chapter and the next
Plate 9.1 — The seven pain points of monolithic hell. Scaling is difficult because modules have conflicting resource requirements (memory-hungry restaurant data versus CPU-intensive image processing); reliability suffers because all modules share one process, so a bug in one crashes them all.

The pain points, briefly

2. Microservices as decomposition into domain services

The microservices architecture is the answer: decompose the monolith into independent domain-oriented components. The components are services, and the connectors are the communication protocols that enable those services to collaborate.

Each service has its own logical view architecture, which is typically a hexagonal architecture: the business logic sits in the core, surrounded by inbound adapters (which handle requests from clients and invoke the business logic) and outbound adapters (which the business logic invokes to reach other services and applications). This is the distributed, domain-partitioned architecture: instead of one WAR file, the system is a set of collaborating services, each owning its slice of the domain.

Key idea

The term ‘microservice’ is a label, not a description.” — Martin Fowler. Nothing in the definition forces services to be tiny: the label describes an architectural style, and getting the granularity right is one of the hardest decisions — as section 3 shows.

3. Microservices and DDD: bounded contexts and granularity

By adopting a DDD approach, a microservice provides the proper modularity and granularity level to be associated with a subdomain and then with a bounded context — or even finer-grained elements, such as aggregates. The microservices and bounded context patterns have a lot in common:

Granularity: deep services and the safe range

At the extremes lie two failure modes. On one side, the big ball of mud — the monolith that never gets decomposed. On the other, the distributed big ball of mud — decomposition too fine-grained, violating consistency boundaries. Between them there is a safe range for decomposing the system.

The module adopts the notion of deep services from The Philosophy of Software Design (Ousterhout): a module is defined by its function (what it is supposed to do) and its logic (how it implements it). A deep service hides its logic behind a small micro-public interface. Shallow services are the reason so many microservices-oriented projects fail: mistaken definitions such as “a service with no more than X lines of code” miss the most important aspect of the architecture — the system.

Exam angle

The threshold upon which a system can be decomposed into microservices is defined by the use cases of the system that the microservices are a part of. Decomposing past the microservices threshold makes services shallow: their interfaces grow back up due to integration needs, and the cost of change goes up again — the distributed big ball of mud. Good design optimises both global and local complexity, not one at the expense of the other.

4. Drivers for modularity and the change scope

Decomposing a system is not an end in itself: the decomposition must serve the drivers for modularity [SAH]:

DriverDefinition
MaintainabilityEase of adding, changing or removing features, as well as applying internal changes such as maintenance patches, framework upgrades and third-party upgrades.
TestabilityEase of testing (usually through automated tests) as well as the completeness of testing.
DeployabilityEase of deployment, frequency of deployment, and the overall risk of deployment.
ScalabilityThe ability of a system to remain responsive as user load gradually increases over time.
ElasticityThe ability of a system to remain responsive during significantly high instantaneous and erratic spikes in user load.

The choice of architecture moves the change scope — the blast radius of a change. In a layered architecture, changes span the whole application: the scope is application-level. In a service-based architecture, changes are contained at the domain level. In a microservices architecture, changes are contained at the function level — a single service can change, be rebuilt and redeployed without touching the rest of the system.

CHANGE SCOPE SHRINKS WITH DECOMPOSITION presentation business logic persistence integration LAYERED change = whole application Catalog service Order service Payment service Shipping service SERVICE-BASED change = one domain Order service Ticket Courier Kitchen service MICROSERVICES change = one function
Plate 9.2 — The change scope shrinks as decomposition deepens: application-level in a layered architecture, domain-level in service-based, function-level in microservices. Containment of change is the point of the architecture, not smallness.

5. Defining the architecture: a three-step process

The module gives a simple, three-step (iterative) process — to be taken as a loose guideline [MP]:

  1. Identifying system operations — distilling the application’s requirements into the key requests, abstracting from specific IPC technologies.
  2. Determine the decomposition into services — two decomposition strategies: by business capabilities or by subdomains (DDD).
  3. Determine each service’s API — assigning system operations to services, and identifying the APIs for collaborating with other services.

The three steps are applied iteratively, and each is examined in the following sections. The running example remains FTGO.

6. Step 1 — identifying system operations

The starting point is the application’s requirements, including user stories and their associated user scenarios. System operations are identified in two sub-steps: (1) build a high-level domain model, and (2) identify the system operations.

Building a high-level domain model

The domain model is derived primarily from the nouns of the user stories. It is typically much simpler than what will ultimately be implemented — the application won’t even have a single domain model, because each service has its own domain model. At this stage the model is useful because it defines the vocabulary for describing the behaviour of the system operations — recalling the DDD ubiquitous language. Standard techniques apply, including Event Storming (Chapter 8).

In the FTGO Place Order story, the scenario “Given a consumer… And a restaurant… When the consumer places an order for the restaurant… Then the consumer’s credit card is authorized…” hints at the existence of classes such as Consumer, Order, Restaurant and CreditCard; the Accept Order story suggests Courier and Delivery. After a few iterations the model consists of those classes and others — MenuItem, Address, OrderLineItem, DeliveryInfo, Location — each with a stated responsibility: the Courier, for instance, tracks the availability of the courier and their current location.

Commands and queries

Once the high-level domain model exists, the next step is to identify the system operations and describe each one’s behaviour in terms of the model: a system operation can create, update or delete domain objects, as well as create or destroy relationships between them. Instead of committing to a specific protocol, the module uses the more abstract notion of a system operation, of two types:

Command specifications

A command has a specification that defines its parameters, return value, and behaviour in terms of the domain model classes. The behaviour specification consists of preconditions (mirroring the givens in user stories) that must be true when the operation is invoked, and post-conditions (mirroring the thens) that are true after the operation is invoked. When a system operation is invoked it verifies the preconditions and performs the actions required to make the post-conditions true.

7. Step 2 — decompose by business capability

Two decomposition strategies exist; each attacks the problem from a different perspective and uses its own terminology, and the end result is the same: an architecture consisting of services organised primarily around business rather than technical concepts.

Business capability = something that a business does in order to generate value. The capabilities of an insurance company include Underwriting, Claims management, Billing, Compliance; those of an online store include Order management, Inventory management, Shipping. Capabilities are identified by analysing the organisation’s purpose, structure and business processes. They are stable: how a business performs a capability changes (deposit check: branch, ATM, smartphone), but the capability itself remains.

Subdomain = a fine-grained area of business activity, identified using the same approach as capabilities (analysing the business and its areas of expertise). Inspired by DDD, it is centred on developing an object-oriented domain model of the application’s problem space, with subdomains and bounded contexts as the two key concepts. The result is likely to be very similar to the capability-based decomposition.

Both strategies are decomposition patterns, and the module’s FTGO subdomains — Order taking, Order management, Kitchen management, Delivery, Financials — are “very similar to the business capabilities described earlier.” Whichever terminology you use, the services end up organised around the business.

From capabilities to services

Each business capability can be thought of as a service — except it is business-oriented rather than technical. Its specification consists of inputs, outputs and service-level agreements; it is often focused on a particular business object (the Claim object for Claims management) and often decomposable into sub-capabilities (Claim information management, Claim review, Claim payment management).

FTGO: BUSINESS CAPABILITIES → SERVICES Supplier management Courier management Restaurant info management Consumer management Order taking & fulfilment Order management Restaurant order mgmt Logistics (availability + delivery mgmt) Accounting Restaurant service Courier / Delivery svc
Plate 9.3 — FTGO’s capability hierarchy and its mapping to services. Some top-level capabilities map to services, others to sub-capabilities; the mapping is somewhat subjective and must be justified: Restaurant and Courier are very different suppliers, so Supplier management is split; the phases of Order taking and fulfilment map to three services; Accounting maps to its own service.

Stability and evolution. The key benefit of organising services around capabilities is that because capabilities are stable, the resulting architecture will also be relatively stable: individual components may evolve as the how aspect of the business changes, but the architecture remains unchanged. It is, however, a continuous process involving iterative refinements: an important step is investigating how the services collaborate in each of the key architectural scenarios — a decomposition may prove inefficient due to excessive interprocess communication (combine services), or a service may grow complex enough to be split.

8. Step 2 — decompose by subdomain, and away from god classes

DDD is quite different from the traditional approach to enterprise modeling, which creates a single model for the entire enterprise — one definition of each business entity such as customer and order. The problems: getting different parts of an organisation to agree on a single model is a monumental task; the model is overly complex for any given part; and the same term may mean different things in different parts (or different terms the same thing). DDD avoids all of this by defining multiple domain models, each with an explicit scope: the bounded context. Each subdomain gets its own domain model.

When using the microservice architecture, each bounded context is a service or possibly a set of services: we can create a microservice architecture by applying DDD and defining a service for each subdomain. DDD and the microservice architecture are in almost perfect alignment:

God classes and the FTGO Order

A god class is a “bloated class” used throughout an application: it implements business logic for many different aspects of the application, and normally has a large number of fields mapped to a database table with many columns. Most applications have at least one — accounts in banking, orders in e-commerce, policies in insurance. Because a god class bundles state and behaviour for many different aspects, it is an insurmountable obstacle to splitting any business logic that uses it into services.

In FTGO, the Order class is the god class. Packaging it into a library with a central Order database violates key microservice principles (any schema change forces every team to update in lockstep). Encapsulating it in an Order Service turns that service into a data service with an anemic domain model — little or no business logic. Applying DDD instead: each service is a separate subdomain with its own domain model, and each service that has anything to do with orders has its own version of the Order class:

ONE GOD CLASS → ONE PER-SERVICE DOMAIN MODEL Order (single domain model) state & behaviour for every part of the app Order Service most complex view of an order Delivery Service simple view renamed Delivery Kitchen Service simpler version: a Ticket pickup address, pickup time, delivery address, delivery time (no attributes of Order) status, requestedDeliveryTime, prepareByTime, line items (unconcerned with payment) decomposition costs: consistency must now be maintained between these objects across services — via event-driven mechanisms such as sagas, and translation at the API gateway
Plate 9.4 — Decomposing the domain. The Delivery Service keeps a simple view of an Order renamed Delivery; the Kitchen Service keeps a simpler version, a Ticket; the Order Service keeps the most complex view. Identifying and eliminating god classes is a must in microservice architecture.

9. Obstacles to decomposition

On the surface, the strategy of defining services corresponding to business capabilities or subdomains looks straightforward. In practice we may encounter several obstacles:

ObstacleWhat it isMitigation
Network latencyCertain decompositions result in a large number of round-trips between two services.Batch API to fetch multiple objects in a single round trip; or combine services, replacing expensive IPC with language-level calls.
Reduced availability (synchronous IPC)The straightforward REST implementation of createOrder() makes the Order Service unavailable whenever any collaborating service is down.Asynchronous messaging, which eliminates tight coupling and improves availability.
Data consistency across servicesSome system operations update data in multiple services; the traditional two-phase commit-based distributed transaction is not a good choice for modern applications.Event-driven sagas: a sequence of local transactions coordinated using messaging, eventually consistent.
Consistent view of the dataEven though each service’s database is consistent, a globally consistent view across databases cannot be obtained.If a consistent view is needed, the data must reside in a single service — rarely a problem in practice.
God classesBloated classes bundle state and behaviour for many aspects of the application, preventing any split of the business logic that uses them.DDD: treat each service as a separate subdomain with its own domain model (section 8).
Editor’s note

The consistency obstacles preview the next chapter: sagas, CQRS and API composition are exactly the patterns that a microservice architecture needs precisely because a distributed system cannot behave like a single database.

10. Step 3 — defining service APIs

Having a list of system operations and a list of potential services, the next step is to define each service’s API. At an abstract level, an API can be defined in terms of two main parts:

The steps

  1. Map each system operation to a service. Many operations map neatly; sometimes the mapping is less obvious — the noteUpdatedLocation() operation relates to couriers (Courier Service) but it is the Delivery Service that needs the courier location, so it is assigned to the service that needs the information. In other situations it may make sense to assign an operation to the service that has the information.
  2. Decide whether a service needs to collaborate with others to implement a system operation. Some operations are handled entirely by a single service (createConsumer()); others span multiple services — implementing createOrder(), the Order Service must invoke the Consumer Service (verify the consumer can place an order, obtain payment info), the Restaurant Service (validate line items, service area, order minimum, prices), the Kitchen Service (create the Ticket) and the Accounting Service (authorize the credit card).
  3. Determine what APIs those other services must provide to support the collaboration.

Finally, the service APIs’ abstract model must be mapped onto specific IPC technologies, either synchronous or asynchronous. Even though the term operation suggests a synchronous request/response mechanism, asynchronous messaging plays a significant role — responsiveness and availability.

11. Interaction styles and IPC technologies

Two independent dimensions classify the interaction styles:

INTERACTION STYLES ONE-TO-ONE request/response timely response, may block, tight coupling async request/response no blocking; reply may be long in coming one-way notification ONE-TO-MANY publish/subscribe zero or more interested services consume publish/async responses publish request, wait a while for responses both dimensions combine REST supports request/response; messaging supports all five styles
Plate 9.5 — The interaction style matrix. One-to-one styles are request/response, asynchronous request/response and one-way notification; one-to-many styles are publish/subscribe and publish/async responses.

IPC technologies

The main IPC technologies for APIs are:

All messages flow through a message broker (MOM). Benefits: loose coupling (clients are unaware of service instances, no discovery needed), message buffering (messages queue up while consumers are unavailable), flexible communication (supports all interaction styles), explicit interprocess communication (messaging does not pretend remote calls are local). Drawbacks: potential performance bottleneck, potential single point of failure (mitigated by highly available brokers), additional operational complexity. Examples: ActiveMQ, RabbitMQ, Apache Kafka; AWS Kinesis, AWS SQS.

Services communicate directly, without an intermediary. Simpler topology, but the buffering, decoupling and availability benefits of a broker are lost: the sender and receiver must both be available, and the client must locate the service instance.

12. Designing and evolving service APIs

The RESTful style

REST (Representational State Transfer) is an architectural style for designing network-based client-server applications at Internet scale, formulated by Roy Fielding as a set of architectural constraints — applied as a whole — that emphasise scalability of component interactions, generality of interfaces, independent deployment of components, and intermediary components. The six constraints: client/server, stateless, cache, uniform interface, layered system, and code on demand (optional). The key constraint for decoupling is the uniform interface, with four requirements: resource identification in requests (URIs), resource manipulation through representations, self-descriptive messages, and HATEOAS (hypermedia as the engine of application state) — the client discovers available actions from server-provided links instead of hard-coding URLs.

In practice, a resource represents a business object (Customer, Product) or a collection; HTTP verbs manipulate resources: GET retrieves a representation, POST creates, PUT updates. The Order Service has POST /orders and GET /orders/{orderId}. Leonard Richardson’s REST maturity model has four levels: Level 0 (single URL, POST specifies the action), Level 1 (resources), Level 2 (HTTP verbs, enabling web infrastructure such as caching), Level 3 (HATEOAS).

Message formats

It is essential to use a cross-language message format, even if all services are in one language today. Text formats (JSON, XML) are human-readable, self-describing and easily backward-compatible, but verbose and parsing-heavy; binary formats (Protocol Buffers with tagged fields, Avro with schema-driven interpretation) are compact and typed, with a compiler generating serialization code — API evolution is easier with Protocol Buffers.

API-first and API evolution

Regardless of the IPC mechanism, the API-first approach is recommended: write the interface definition first using an IDL (Interface Definition Language) — the standard for REST is the Open API Specification (evolved from Swagger), and AsyncAPI is the analogous initiative for asynchronous, message/event-based APIs — then review it with client developers, and only then implement the service.

APIs invariably change over time, and in a microservices application clients cannot be forced to upgrade in lockstep: a strategy is needed. The Semantic Versioning specification (MAJOR.MINOR.PATCH) is the guide: increment MAJOR on incompatible changes, MINOR on backward-compatible enhancements, PATCH on backward-compatible bug fixes. Prefer backward-compatible changes (add optional request attributes, add response attributes, add operations), which work for older clients provided they observe the Robustness principle (“be conservative in what you do, be liberal in what you accept from others”). For breaking changes, the service must support old and new versions simultaneously: embed the major version in the URL (/v1/…), or use content negotiation with the version in the MIME type. The service’s API adapters contain the translation logic between versions, and the API gateway will almost certainly have to support numerous older versions.

13. The missing aspect: concurrency models

The module closes by pointing at an open problem. A microservice’s behaviour depends on the concurrency model governing the execution of operations, including the business logic. This calls for a clear control architecture defining (1) how multiple concurrent requests should be modelled and managed, and (2) how interaction with external services should be modelled and managed — with strong impact on correctness, performance and responsiveness.

The concurrency model is a cross-cutting concern through layers, including the business logic — and the problem is that the domain model pattern used in DDD does not consider concurrency. The basic control architectures:

ModelDescriptionIssues
SequentialA single control thread serves every request; synchronous, blocking; compatible with the basic OOP model.Poor performance, responsiveness, reactivity in general.
Synchronous, thread-basedA thread pool serves requests concurrently; synchronous with blocking calls; locks, semaphores, monitors govern competition and cooperation.Race conditions must be managed explicitly.
Asynchronous, event-basedEvent loop with background threads executing async tasks; never-blocking rule.No race conditions by construction, but async programming complexity.
Asynchronous, reactive/flow-basedReactive programming: asynchronous, declarative model based on asynchronous flows.New paradigm to master.
Asynchronous, message-basedThe control loop is a message loop; the actor model.Fits event-driven architectures.
The open challenge

The domain model pattern promoted in DDD is based on a simple OO metamodel that does not capture concurrency. The open question, taken up again with reactive architectures in Chapter 11: how to specify and model concurrency aspects in business logic, and with which metamodel?

Check your understanding

List the benefits of the early FTGO monolith and the pain points of monolithic hell.

Benefits: simple to develop, easy to make radical changes, straightforward to test, straightforward to deploy, easy to scale. Pain points: overwhelming complexity (a downward spiral), slow development (long edit-build-run-test loop), long path from commit to deployment (monthly releases, unreleasable builds, painful merges, days-long testing cycles), difficult scaling (conflicting resource requirements), challenging reliability (no fault isolation, memory leaks crash all instances), and technology lock-in.

How do components and connectors relate in the microservices architecture, and what is each service’s typical internal architecture?

The components are services and the connectors are the communication protocols that enable services to collaborate. Each service typically has its own hexagonal (logical view) architecture: business logic in the core, inbound adapters handling client requests, outbound adapters invoked to reach other services and applications.

What do microservices and bounded contexts have in common, and how can they differ?

Common: both are physical boundaries, both are owned by a single team, and conflicting models cannot be implemented in one microservice without complex interfaces. Difference: a bounded context is not necessarily mapped into a single microservice — it could be mapped into more than one — but each microservice is meant to function as a strong consistency boundary.

What are the two failure modes at the extremes of granularity, and what defines the safe range?

The big ball of mud (a monolith never decomposed) and the distributed big ball of mud (decomposition too fine-grained, violating consistency boundaries). The safe range is defined by the use cases of the system: services should be deep, hiding complex logic behind small micro-public interfaces; decomposing past the microservices threshold makes services shallow, their interfaces grow back due to integration needs, and the cost of change rises again.

State the five drivers for modularity.

Maintainability (ease of adding/changing/removing features and internal changes), testability (ease and completeness of testing), deployability (ease, frequency and risk of deployment), scalability (remaining responsive as load gradually increases), elasticity (remaining responsive during high instantaneous and erratic load spikes).

Describe the three-step process for defining an application’s microservice architecture.

(1) Identify system operations — distil the requirements into key requests, abstracting from specific IPC technologies: first build a high-level domain model from the nouns of user stories, then identify commands (from the verbs) and queries, each command specified by preconditions and post-conditions. (2) Determine the decomposition into services — by business capabilities or by subdomains (DDD). (3) Determine each service’s API — assign system operations to services, decide collaborations, and identify the APIs those collaborations require.

Contrast decomposition by business capability and by subdomain.

A business capability is something a business does to generate value, identified by analysing purpose, structure and business processes; capabilities are stable over time. A subdomain is a fine-grained area of business activity, identified by analysing the business and its areas of expertise, and comes from DDD with its domain model and bounded context. Both attack the problem from different perspectives with their own terminology, but the end result is the same: services organised around business rather than technical concepts — FTGO’s subdomains (Order taking, Kitchen management, Delivery…) closely mirror its capabilities.

What is a god class, and what are the three candidate solutions for the FTGO Order god class?

A god class is a bloated class used throughout an application, implementing business logic for many different aspects, with many fields mapped to a wide database table — an insurmountable obstacle to decomposition. Candidate solutions: (1) package it in a library with a central Order database — violates microservice principles and couples teams in lockstep; (2) encapsulate it in an Order Service — yields a data service with an anemic domain model; (3) apply DDD: each service is a separate subdomain with its own domain model — Delivery keeps a simple Delivery view, Kitchen keeps a Ticket, Order keeps the most complex view.

List the five obstacles to decomposition and their mitigations.

(1) Network latency — batch APIs or combine services. (2) Reduced availability from synchronous IPC — asynchronous messaging. (3) Data consistency across services — event-driven sagas instead of 2PC. (4) No globally consistent view of data — keep data needing consistency in one service. (5) God classes — DDD per-service domain models.

What two parts define a service API abstractly, and how are system operations assigned to services?

An API is defined by operations (commands and queries; corresponding to system operations or supporting collaboration) and events (published to collaborate with other services, e.g. sagas and CQRS views, or to notify external clients). System operations are assigned by deciding the initial entry point: many map neatly; when ambiguous, assign to the service that needs the information the operation provides (e.g. noteUpdatedLocation() goes to Delivery), or in other situations to the service that has the information.

List the five interaction styles and the main IPC technologies.

One-to-one: request/response, asynchronous request/response, one-way notification. One-to-many: publish/subscribe, publish/async responses. Main IPC technologies: REST (HTTP-based RPI or RESTful style), messages and MOM (asynchronous, broker-based or brokerless), gRPC, GraphQL.

What are document, command and event messages, and what are the two kinds of channels?

A document message contains only data, and the receiver decides how to interpret it (the reply to a command is a document). A command message is the equivalent of an RPC request, specifying the operation and its parameters. An event message indicates that something notable has occurred — often a domain event representing a state change. Channels: point-to-point delivers each message to exactly one consumer (used for one-to-one styles, e.g. command messages); publish-subscribe delivers each message to all attached consumers (used for one-to-many styles, e.g. event messages).

State the four benefits and three drawbacks of broker-based messaging.

Benefits: loose coupling (no discovery needed), message buffering (messages queue while consumers are unavailable), flexible communication (all interaction styles), explicit interprocess communication (no false illusion of locality). Drawbacks: potential performance bottleneck, potential single point of failure, and additional operational complexity.

What are the six REST constraints, and what does the uniform interface require?

Client/server, stateless, cache, uniform interface, layered system, code on demand (optional). The uniform interface requires: resource identification in requests (URIs), resource manipulation through representations, self-descriptive messages, and HATEOAS (hypermedia as the engine of application state).

How does semantic versioning guide API evolution, and what mechanisms support multiple versions?

Version numbers are MAJOR.MINOR.PATCH: increment MAJOR on incompatible changes, MINOR on backward-compatible enhancements, PATCH on bug fixes. Prefer additive, backward-compatible changes with default values and tolerance per the Robustness principle. For breaking changes, support old and new versions simultaneously: major version in the URL path (/v1/), or version in the MIME type via content negotiation; adapters translate between versions and the API gateway may support many older versions.

Name the five basic control architectures and the open challenge about concurrency.

Sequential (single thread), synchronous thread-based (thread pool with locks), asynchronous event-based (event loop), asynchronous reactive/flow-based (reactive programming), asynchronous message-based (message loop, actor model). The open challenge: the DDD domain model pattern is based on a simple OO metamodel that does not capture concurrency, so it is an open question how to model concurrency aspects in business logic — taken up by reactive architectures.