Specy organizes software designer intent into three abstraction layers for a good separation of concerns: a Product Requirements Document (PRD), a set of System Requirements written with the EARS syntax, and a Domain Model that follows the Domain-Driven Design approach. Each layer speaks to a different audience and answers a different question, yet together they form a whole and a traceable chain that connects product decisions to running code.
The three layers at a glance
Software that matters is specified on three levels. Each level formalizes a distinct kind of decision; mixing them produces documents that are simultaneously too vague to build from and too concrete to reason about.
Each layer adds precision without repeating what the layer above already said. The PRD declares “riders should be able to cancel for free before a driver is assigned.” A system requirement formalizes that into a testable EARS statement. The domain model implements it as a state guard, a command, and an event. Different languages, same intent.
Example:
- PRD says: “Free cancellation before driver assignment reduces commitment anxiety.”
- System requirement says: “While the ride has no assigned driver, if the rider cancels, then the system shall terminate the request with no fee.”
- Domain model says:
RideRequestentity, stateunassigned, commandCancelRequestByRider, eventRideRequestCancelledByRider, invariantnoFeeBeforeAssignment.
Product Requirements — what the product should be
The PRD layer captures what the product should be, for whom, and why. It is upstream of everything else and it speaks the language of product management — personas, jobs, goals, features, metrics — not the language of engineering. Its audience is product managers, designers, leadership, and engineers aligning on shared intent before anyone opens a specification.
The PRD answers four questions:
- Why does this product exist? — through a Problem Statement framed as situation, complication, and key question.
- Who is it for? — through Personas anchored in research, and Jobs that describe what those personas are trying to accomplish regardless of any product.
- What does it do about it? — through Features, User Stories, and User Journeys that describe capabilities from the user’s perspective.
- How will we know it worked? — through Goals, Success Metrics, and Hypotheses that make the bet testable.
A PRD also carries what won’t exist: non-goals, constraints, risks, assumptions, and open questions. These prevent scope creep and surface uncertainty early rather than late.
The PRD does not specify system behavior. A feature like “Free cancellation” is an aspiration until a requirement expresses it precisely enough to be verified.
Concrete syntax lives in .prd files. See the full model in Product & Problem and the rest of the PRD section.
System Requirements — what the system shall do
The System Requirements layer translates product intent into testable system obligations written in EARS (Easy Approach to Requirements Syntax). EARS was developed at Rolls-Royce and published at IEEE RE ‘09; it addresses eight known deficiencies of unconstrained natural language: ambiguity, vagueness, complexity, omission, duplication, wordiness, inappropriate implementation, and untestability.
Every EARS requirement is a single sentence in strict temporal order:
[While <precondition>,] [When|If|Where <trigger>,] the <system> shall <response>.
Six patterns cover every kind of requirement: Ubiquitous (always true), State-driven (While), Event-driven (When), Unwanted (If-Then), Optional (Where), and Complex (combinations). Each requirement carries an id, a rationale (::), and a MoSCoW priority (must / should / could / wont).
Requirements are grouped into Requirement Sets scoped to a bounded context or the organization. A set may declare prd-source to point back to the .prd file that originated it, making the provenance chain file-resolvable.
This layer includes non-functional requirements too — performance budgets, availability SLAs, security obligations, compliance mandates. NFRs use the same EARS syntax; what makes them “non-functional” is that they constrain how well the system behaves rather than what it does.
Concrete syntax lives in .sysreq files. See EARS Fundamentals and EARS Patterns.
Domain Model — how the system realizes the intent
The Domain Model layer is where obligations become structure and behavior. It uses Domain-Driven Design vocabulary: bounded contexts, aggregates, entities, value types, commands, queries, events, invariants, policies, services. This is the layer that engineers read most often and the layer closest to the code.
A domain model declares:
- Structural boundaries — organization, bounded contexts, modules, interfaces.
- Concepts with identity — entities and aggregates, their operations and lifecycles.
- Concepts defined by value — immutable value types with transactional constructors.
- Communication — commands (intentions), queries (current state), and events (past facts), including internal, external, error, and temporal events.
- Rules — invariants (synchronous, within one aggregate), reactions/policies (asynchronous, across aggregates), and agreements backed by reconciliations for cross-aggregate truths.
- Provenance — a
satisfies [REQ-…]list on every element that realizes a system requirement, and arequirements-sourcepointer to the originating.sysreqfile.
The domain model is independent of the technology stack. The same domain model can be realized as microservices, a monolith, an event-sourced system, or a classic CRUD application — those are delivery concerns, not modeling concerns.
The model is also independently verifiable by construction. Alongside structure and behavior, a Specy domain model exposes its own verifiable surface: a closed set of Properties that turn the model into something a machine can check. Each property names a claim, scopes it precisely, and writes the predicate that must hold.
- Invariants — synchronous state predicates that must hold at every observable point within an aggregate. Each invariant declares an enforcement strategy (rejection, compensation, or alert), so the response to a violation is part of the contract, not an implementation choice.
- Agreements — cross-aggregate predicates that no single transaction can verify alone. Each agreement names its participants and its inconsistency window, and is maintained by exactly one reconciliation — making eventually consistent an auditable claim rather than a hand-wave.
- Preconditions & postconditions — the contract on every operation: what must hold to call it, what will hold once it returns. The pair is the operation’s promise to its caller, independent of the body.
- State-machine constraints — lifecycle properties on entities: which states are reachable, which transitions are legal, which guards must hold, which invariants apply only while the entity occupies a given state.
- Escalation chains — what happens when a reconciliation fails: an ordered sequence of retry, alternative compensation, alert, suspend, or manual-intervention steps that must terminate.
Because every property carries an explicit predicate and scope, the model is a direct target for property-based testing: a generator produces valid inputs from the declared types, the runner exercises operations and transitions, and the checker asserts each predicate after every observable point. There is no hidden rule for the test framework to discover — the contract is in the .domain file. The same declarations drive runtime observability: invariants and agreements can be evaluated against live event streams to detect drift between the model and production. Built-in verifiability means the metamodel doesn’t just record what the system should do — it records the predicates that prove it does, in a form a machine can run.
Concrete syntax lives in .domain files. See Organization & Contexts for the entry point and Properties for the verifiable surface.
Separation of concerns
Each layer has a job, an audience, and a vocabulary. Crossing the line makes every layer weaker.
| Layer | Question it answers | Audience | Vocabulary | Must not contain |
|---|---|---|---|---|
| 01 PRD | Why / for whom / what product | Product, design, leadership, engineering | Personas, jobs, goals, features, metrics | System behavior, data schemas, error handling |
| 02 System Requirements | What the system shall do | Engineering, QA, compliance, auditors | EARS sentences, priorities, rationales | Product rationale, UX detail, implementation choices |
| 03 Domain Model | How obligations are structured and realized | Engineering, architects | Bounded contexts, entities, commands, events, invariants | Product justification, user-facing copy |
Example:
- The PRD doesn’t say “the
CancelRequestByRidercommand transitions theRideRequestfromunassignedtocancelled.” That belongs to the domain model. - A system requirement doesn’t say “a rider wants to feel free to change their mind.” That belongs to the PRD — the EARS statement doesn’t care why the rule exists, only that it is testable and complete.
- The domain model doesn’t say “this feature drives a 20% lift in conversion.” That belongs to the PRD as a hypothesis.
Keeping each layer honest lets the team change one without forcing rewrites in the others. A change in product strategy rewrites PRD features; most existing requirements and domain elements survive untouched. A change in technology stack rewrites code; the domain model, requirements, and PRD survive untouched.
Traceability across layers
The layers are connected through file-resolvable provenance markers, not foreign-key constraints. Each layer references the layer above by name, and the chain can be followed by any agent or tool with filesystem access.
content/specs/order.prd ← PRD (Feature, User Story, AC, Constraint, Goal)
▲
│ source "Feature: Free cancellation — AC: No fee before assignment"
content/specs/order.sysreq ← EARS requirement (REQ-ORD-020)
▲
│ satisfies [REQ-ORD-020]
content/specs/order.domain ← Domain model (command, event, invariant)
▲
│ realized-in
code ← Implementation
Two queries run across this chain without any database:
- Coverage — Which
mustrequirements have no domain element declaringsatisfies [REQ-…]? Those are gaps. - Impact analysis — If a PRD feature changes, which requirements reference it in their
sourcefield, and which domain elements satisfy those requirements? Those are the candidates for revision.
The same traceability supports runtime observability: event-driven and unwanted requirements can be verified in production by tracing the events declared in the domain model and comparing their occurrence against the obligations in the requirement set.
Ubiquitous language across layers
Provenance markers thread the layers structurally. The ubiquitous language threads them semantically. A word that names a thing in the domain model — Ride, RideRequest, Rider, cancellation fee, driver assignment — must mean the same thing when it appears in a system requirement, in a user story, in a feature description, and in a conversation with a domain expert. The discipline of using one shared vocabulary across product, engineering, and operations is what turns three otherwise-disconnected documents into one coherent specification.
Each layer applies the rule with its own grain:
- PRD speaks the user-facing version of the language. A persona, a job, a feature, a user story — they should all use the same noun for the same thing the engineering team will model. “Cancel a ride” in the PRD is the same act that the domain model formalizes as
CancelRequestByRiderand that a system requirement obligates with “the system shall terminate the request with no fee.” If product invents a new word for an existing concept, the trace from feature to requirement to code becomes a translation problem. - System requirements are the strictest test of language alignment. EARS sentences are short and literal — “the system shall …” — so any drift between the noun in the requirement and the noun in the domain shows up immediately. If a requirement says “the rider” and the domain model has only
Customer, one of the two is wrong. - Domain models form the semantic layer where the language is defined. Every entity, command, event, invariant carries a name; that name is the canonical form. The bounded context is what scopes the language: inside one context, every word has one agreed meaning. When a word changes meaning at a context boundary — “order” in the order context vs “order” in the fulfillment context — the metamodel forces an explicit translation through a context-map pattern, most often an Anti-Corruption Layer.
Why this matters in practice. Provenance lets you trace a feature to its requirements and its code; ubiquitous language lets you read the trace without a translation key. A team whose three layers share vocabulary can hold a conversation in one room — product, engineering, and ops nodding at the same nouns. A team whose layers each speak their own dialect spends meetings explaining what the other meant before any decision is made. The first habit compounds; the second taxes every conversation.
The simplest test: take any noun from a recent PRD feature. Search the .sysreq files for it. Search the .domain files for it. If you find the same word in all three places, the language is aligned. If you find three different words for the same thing, the alignment work is overdue — and that work is shared between domain modelers (who name canonically) and product writers (who must adopt the canonical names rather than coining new ones).
Why three layers and not one
A single document that mixes product rationale, system obligations, and domain structure is familiar — and it fails predictably. It is too vague for engineers because the EARS precision is diluted by product prose; too concrete for product managers because implementation detail obscures strategic intent; and too volatile for architects because every product pivot rewrites the document.
Splitting intent into three layers yields three compounding wins:
- Each layer can evolve at its own rate. Product strategy turns on quarters. System requirements turn on feature cycles. Domain models turn on architectural decisions. The layers shouldn’t share a change cadence.
- Each layer gets the right reviewers. Product reviews the PRD. Engineering and compliance review the system requirements. Architects review the domain model. Nobody is forced to read what they can’t evaluate.
- Each layer enables a specific verification loop.
- Build (intent → code) reads requirements and produces a domain model whose elements declare
satisfiesback to requirement ids. - Check (code → intent) traverses
satisfieslists to prove everymustrequirement is covered. - Observe (runtime → intent) watches event traces against event-driven and unwanted requirements to detect drift.
- Build (intent → code) reads requirements and produces a domain model whose elements declare
Three layers is the minimum that separates why, what, and how without collapsing any of them. Add a fourth (say, a separate UX spec) only when a stakeholder group demonstrably can’t work from any of these three — and even then, link it into the provenance chain with the same discipline.
The rest of this reference covers each layer in depth. Start with Product & Problem for the PRD, EARS Fundamentals for System Requirements, and Organization & Contexts for the Domain Model.