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

  • Collection selection from Chapter 9 is complete.
  • Failure and business-commitment boundaries from Chapter 14 are understood.
  • The Maven example in examples/order-fulfillment-control-plane can run.

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:

  • creation, start, and close of DsmRuntime;
  • clusterId and serviceId;
  • three stable locators and schema IDs;
  • entity, codec, and spec definitions for Register, Lease, and CRDT;
  • mapping of three typed handles into the domain port.

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

  • Copy locator strings into FulfillmentService, change a collection ID, and count business files touched.
  • Reduce LeaseAcquireResult to a boolean and try to express uncertain=true.
  • Omit runtime.start() and locate the lifecycle failure.
  • Let business code call requestCounter.state() and observe CRDT types in domain tests.

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

  • Domain ports own business language; adapters own DSM mechanism.
  • Locator, schema, codec, and lifecycle belong in the composition root.
  • Failure translation retains uncertainty and the fencing token.
  • Fakes and real Runtime tests answer different questions.

Next

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