Skip to content

Data Authority Module Pattern

A Data Authority Module is the primary pattern for implementing modules that own and manage a set of entities. It has a well-defined four-layer structure and a standard pattern for exposing CRUDQ operations.

Module Structure

A Data Authority Module consists of four distinct layers:

Responsible for:

  • Handling communication protocols that expose the module’s API Endpoints
  • Protocol data marshalling and unmarshalling
  • Entity identity extraction from requests
  • Routing requests to the appropriate operations in the Service Layer
  • Conversion between wire timing/concurrency models and internal Service concurrency model (synchronous vs. asynchronous, multipart messages)

Responsible for:

  • Definition of data structure, validation, and behavior of individual entities and value objects managed by the service
  • All business logic implementable by traversing the object graph reachable from an entity instance
  • Transactional boundary of the module — individual operations typically represent transactional updates
  • Coordination of persistence operations across multiple Universes (e.g., referential integrity between separate entities)
  • Coordination of proxy operations that invoke other modules’ services, including distributed commit/rollback (saga patterns)
  • Generation of entity identities and ADNs; interpretation of ADNs to resolve entity values
  • Generation and emitting of Notifications in response to changes

Responsible for:

  • Storage and retrieval of entity values from persistent storage
  • Management of bitemporal changes to entity values
  • Validation and management of entity collections (concept of Universe) including uniqueness and ordering
  • Defining EntityServiceConfiguration per entity type to build structured locator translators that map JSON field names to database columns for the Query DSL

Responsible for:

  • Providing access to API Endpoints of other modules
  • Allowing the Service Layer to interact with other modules without knowing their implementation or runtime location

API Endpoints expose module capabilities to other modules and external systems. Each module may expose multiple API Endpoints.

Specify a set of operations, each defined by a Pair<Request, Response>. Operations always specify a response (which may be a simple acknowledgement). May be synchronous or asynchronous.

PlantUML diagram

Implemented using:

  • HTTP/REST for Request/Response
  • gRPC for Request/Response
  • SQS, WebSockets, Kinesis, or Kafka for Streaming

Produce or consume a stream of Notifications. Usually asynchronous.

LayerKey Responsibilities
Protocol AdaptorWire serialization/deserialization, protocol-specific concerns, request routing
ServiceBusiness logic, transaction boundaries, multi-universe coordination, notifications
PersistenceBitemporal storage, collection management (Universe), scoping
ProxyAbstracted access to other modules’ endpoints

Arda’s Ktor-based implementation follows this wiring pattern (see Ktor Module Wiring):

fun Application.myModule(
inComponent: ComponentConfiguration,
locator: EndpointLocator.Rest,
cfg: ModuleConfig,
authentication: Authentication,
injectedUniverse: MyUniverse? = null,
injectedService: MyService? = null,
): MyService {
val db: Database = DataSource(cfg.dataSource!!.db, cfg.dataSource!!.pool)
.newDb(cfg.dataSource!!.flywayConfig)
val service = injectedService ?: MyService.Impl(injectedUniverse ?: MyUniverse(), db)
val endpoint = MyEndpoint.Impl(cfg, locator, service)
MultiEndpointKtorModule(inComponent, cfg, authentication, listOf(endpoint))
.configureServer(this)
return service
}

See examples in the item-data-authority repository.

The Universe concept represents the complete collection of entities managed by the persistence layer for a module. Its design is covered in Persistence: Universe Design.

The universe-definition.md source document is marked “To be added” — content is available in the universe-design.md page.