ARC 4Integrate Application EngineeringChapters 15–18

Chapter 15 — Isolate DSM Details with a Domain Adapter

Chapter objective: After this chapter, a developer can wrap the three typed DSM handles in a domain port and keep locator, lifecycle, and failure translation inside the adapter boundary.

Learning objectives

  1. Identify the maintenance cost of DSM APIs leaking into business code.
  2. Design a FulfillmentCoordination port in business language.
  3. Centralize Register, Lease, CRDT handles and locators in a composition root.
  4. Validate domain decisions and real Runtime behavior separately.

Prerequisites

Case progress

Direct collection calls in the first three arcs exposed semantics. In an application, the fulfillment use cases say "publish a route," "claim a shard," and "record a request." They do not refer to shared/gateway/route-hints or LeaseAcquireOptions.

The domain port owns language; the adapter owns mechanism

Domain port and DSM adapter boundary

The book example defines a small port:

public interface FulfillmentCoordination {
    void publishRoute(String serviceKey, String address);
    Optional<String> routeFor(String serviceKey);
    ShardClaim claimShard(String shardId);
    long incrementRequestCount();
}

The port returns no DsmRegister, LeaseSnapshot, PnCounterState, or CollectionLocator. A fake can test the business service directly. The DSM adapter translates the Lease result into ShardClaim(granted, uncertain, fencingToken, reason).

One composition root

DsmFulfillmentModule owns:

Locator and schema remain important contracts. The adapter owns them so they do not spread through controllers, services, and jobs.

Failure translation preserves uncertainty

A Lease acquire result contains granted, uncertain, a snapshot, and a reason. Reducing it to a boolean prevents the caller from distinguishing explicit rejection from an uncertain result. The domain result retains information required by a business decision while preventing arbitrary handle access.

The fencing token also crosses the port into a protected downstream write. Hiding it would break the Chapter 7 boundary. The example returns the token but does not implement a real downstream guard, so the acceptance card does not claim external fencing.

Two test classes prove two layers

  1. RecordingCoordination shows that FulfillmentService depends only on the domain port: E1.
  2. A real DsmRuntime and three collection types validate the adapter in one process: E2.

Only the second makes business decisions and DSM assembly inseparable; only the first can stay green while a real spec, codec, or Lease fails. Both are needed.

Counterexample and fault injection

Experiment

cd examples/order-fulfillment-control-plane
mvn clean test

Inspect port-contract.json. Each business action identifies domain input/output, DSM handle, and external responsibility outside the adapter.

Experiment acceptance card

Field Content
Command cd examples/order-fulfillment-control-plane && mvn clean test
Input or fault fake port, real single-node Runtime, and three typed handles
Observable result domain service imports no DSM types; adapter performs route, Lease, and counter operations
Evidence level E1 domain unit test plus E2 single-process Runtime behavior
This experiment does not prove Multi-node propagation, repair, real network behavior, downstream fencing, or production assembly

Review

Next

Chapter 16 replaces manual assembly with Spring Boot properties and conditional beans, then validates context startup and lifecycle.