Skip to content

Module Concept

A Module is the core unit of Functional Decomposition for Arda’s system. It declares the resources it requires, encapsulates its state, contributes a cohesive set of capabilities, and interacts with other Modules only through well-defined Services exposed via Endpoints. The Module is the minimum unit of independent versioning and deployment.

For the broader Functional Viewpoint — the System / Domain / Module / Service / Endpoint hierarchy, the canonical URL formula, and the per-surface naming rules — see Functional Decomposition. For the deployment-side counterpart that packages Modules and provisions the resources they declare, see Component Concept.

Modules are grouped into Functional Domains that represent major areas of related product capabilities. The design aims for:

  • High cohesion within each Module
  • Low harmful coupling between Modules
  • High coherence of the artifacts a Module defines and implements

A Module encapsulates a portion of the system state accessible only through the Module’s Services. The minimum encapsulation requirement is at the Module level. Where Modules support multiple Services, those Services own their own transactional boundaries; exceptions for performance (e.g., foreign keys between Services within a Module) must never break encapsulation at the Module level.

  • A Module is the basic unit of change and version control. The Module’s version travels with every contract it exposes; all artifacts that define it or are generated from it carry the Module version.
  • A Module is typically implemented within a single technology ecosystem (Kotlin/JVM, TypeScript/Node.js).
  • A Module declares the resources it requires; a Runtime Component provisions or binds them. The Module’s HOCON or YAML configuration names the logical resources (a database schema, a set of secrets, a remote HTTP server) it needs; the Component that packages the Module provides the concrete provisioning context (the database server URI, the secret-store binding, the HTTP client defaults). The Helm template at deploy time and the Kotlin wiring at start-up time combine the two to produce the bound K8s/AWS resources and the in-pod objects the Module’s code consumes. This declare → provide → bind pattern is the inversion-of-control split between the functional hierarchy (where Modules live) and the deployment topology (where Components live). Components are orthogonal to the functional hierarchy and never appear in any name a Module exposes.

Modules interact through four well-defined mechanisms:

Services and Endpoints : A Module invokes operations on Services exposed by other Modules. A Service is a transactional unit of business logic — operations that must share a transaction belong to the same Service. An Endpoint is the protocol-bound surface that exposes a Service’s operations to callers; a Service may expose more than one Endpoint when the protocol-binding or authentication needs differ (for example, an in-perimeter JWT-authenticated REST Endpoint plus a separate webhook Endpoint authenticated by a shared secret). Each Endpoint groups multiple operations (a sub-tree of HTTP routes), not a single operation.

References : A Module may keep references to entities managed by other Modules. A Reference at the system level must uniquely identify the entity and the instance of the Module that manages it.

Data Types : For complex interactions, Modules exchange structured information via Data Types. Data Types must have strict value semantics:

  1. Two instances are equal if and only if the information they contain is equal.
  2. Data Type instances are immutable — once created, they cannot be changed.

Bindings : The expression of a Module’s Data Types and Services in a particular technology, protocol, and address space. A Binding defines how a Module invokes operations using a specific protocol (e.g., REST over HTTP), data encoding (e.g., JSON on UTF-8), and base URL.

When modules share a technology ecosystem, sharing Data Types and Service Accessors via shared libraries avoids harmful duplication. Arda’s products are primarily implemented in:

  • Kotlin/JVM for backend modules
  • TypeScript/NodeJS/React for frontend modules

Interactions between modules use:

  • RESTful APIs over HTTP/1.1 with JSON for frontend-to-backend and external system access
  • gRPC over HTTP/2 with Protobuf for backend-to-backend access

A backend module may publish:

  1. OpenAPI specifications for RESTful APIs
  2. Protobuf IDL files for gRPC APIs
  3. A Kotlin library with ADT-like interfaces and data classes for the information the Module supports
  4. Binding libraries that bridge ADT types to wire representations
  5. Persistence mapping libraries for shared embedded types

A module may not publish all of the above. The minimum is to publish IDL specifications for the protocols it supports.

Data Types range in specificity:

LevelExamples
Primitive typesString, Int
Common technology typesURI, UUID, DateTime, Email
Common business conceptsAddress, Money, Length, GeoLocation
Industry-specific conceptsWorkOrder, BillOfMaterials
Module-specific conceptsUserAccount, KanbanCard

For intermediate types, placement options are: the module that first introduces the concept, a dedicated “sister” module, a domain-level shared module, or the system-level common-module. Placement may be promoted over time as usage becomes more widespread.

Cohesion : How closely related are the responsibilities and capabilities within a system element. Indicated by the number of unrelated primitives or workflows. Related to the quality of decomposition of the containing element.

Coherence : How consistent all artifacts contributing to a capability are. Indicated by exceptions, special cases, and different ways of doing things. Related to the design of the artifacts that support the capability.

Coupling : How interdependent two system elements are. Indicated by cross-references (explicit or implicit assumptions about behavior). The goal of good design is to minimize harmful coupling.

Encapsulation : A portion of system state accessible and modifiable only through a Service, which maintains the integrity of the encapsulated state.

Service : A unit of business logic exposed by a Module. Owns a transactional boundary — operations that must share a transaction belong to the same Service. A Service may own multiple entities when those entities must be mutated atomically. Future-facing: the unit at which fine-grained authorization (ABAC) is enforced.

Endpoint : A protocol-bound surface that exposes a Service’s operations. Defines the authentication mechanism, the wire protocol (REST/HTTP, gRPC, webhook, etc.), and the address space. An Endpoint groups multiple operations — a sub-tree of HTTP routes for REST Endpoints — not a single operation. A Service may have more than one Endpoint when callers come through distinct protocol or authentication regimes.

DataAuthority : A specific kind of Service that provides bitemporal CRUDQ over an Entity. The standard layered structure for a DataAuthority Service is described in the Data Authority Module Pattern. A Module’s primary Service is often a DataAuthority, but Services need not be DataAuthorities (operational services, processing services, and event-ingest services are all valid non-DataAuthority Services).

Operation : A typed, named operation exposed within an Endpoint. Has a unique name (within its Endpoint), a typed input parameter, and a typed output parameter. In REST Endpoints, operations correspond to specific method-and-route pairs in the Endpoint’s sub-tree of routes.

Behavior : The observable effects and results of invoking an operation — changes to internal state, invocations of other Services, interactions with external systems, and output values.