12 min readCalm pace · scan the outline anytime

Spiral one-shot apply (full architecture)

Self-contained Modular Hexagonal DDD + Spiral 3 apply playbook for AI agents.

Modular Hexagonal DDD — ONE-SHOT APPLY (spiral)

Self-contained. An AI agent should fetch this file only and execute it. Core version: 1.0.0-draftShort HTML entry: /spiral · This file: /apply/spiral.full.mdAI-neutral: assistant files are optional; architecture does not require any AI vendor. Host baseline researched: see Part C header (versions as of 2026-08).


0. Agent mandate

You are bootstrapping a spiral application repository (not the docs site).

  1. Treat this document as the complete working standard for this session.
  2. Execute Part D in order after reading Parts A–C.
  3. When writing module code, obey Part B (Core) and Part C (host).
  4. Do not create a git commit unless the human explicitly asks.
  5. Rename illustrative contexts Ordering, Warehouse, Directory, Identity to the human’s domains.
  6. If AI vendor is none, skip Part E.

Programmer prompt

Apply Modular Hexagonal DDD from <ORIGIN>/apply/spiral.full.md
AI assistant: <cursor|claude|gemini|copilot|generic|none>
Bounded contexts: <list>
Do not commit unless I ask.

Part A — Design test

If the peer module is deleted and replaced by HTTP, does this module’s Application and Domain still compile?


Part B — Complete Core architecture

B1. Purpose

Build applications as replaceable bounded-context modules using Hexagonal Architecture (ports & adapters) + DDD. This docs site is the sole canon; consuming repos keep thin pointers, not a forked essay.

B2. Module layout (MUST)

{Module}/
  Application/     # UseCases, Application DTO, Providers / composition root
  Domain/          # Entities, Ports, Events, Enums, Domain DTOs
  Infrastructure/  # Persistence adapters, ExternalServices (ACL), Config, listeners
  UI/              # Controllers, Routes, validation, admin UI, console
Shared/            # Promoted technical ports/adapters only (after promote rule)

Host roots vary (app/Modules, src/Modules, packages). Roles do not.

B3. Strictness ladder (MUST)

StrictnessAreaAllowedForbidden
HardestDomainOwn Domain types + Shared kernel portsOther modules; host facades/helpers; ORM; framework traits on Domain Events
StrictApplication Use Cases / App DTOsOwn Domain + Shared portsFacades, ORM, foreign Application/Domain, peer *ModuleInterface
RelaxedComposition rootContainer binds, routes, config mergeBusiness rules
OuterInfrastructure & UIFramework, ORM, HTTP, ACL adapters, adminLeaking ORM/foreign Domain into Domain/Use Cases

Dependency direction: Domain ← Application ← Infrastructure/UI. Ports in Domain; adapters in Infrastructure.

B4. Golden flow (MUST)

UI → Application DTO → first-level *UseCase::__invoke
  → Domain (Entity / Domain service)
  → Domain Port
  → Infrastructure adapter (ORM / ACL / HTTP)
  → back as Entity / result
  → UI presents

B5. Use Cases & DTOs (MUST)

  • First-level: Application/UseCases/{Capability}/SomethingUseCase with single public __invoke.
  • Nested helpers: no UseCase suffix; not called from UI.
  • Mirror {Capability}/ for DTOs and Ports.
  • ACL need ports: Domain/Ports/Acl/. Module façades: Domain/Ports/Module/.
  • Never create a literal Feature/ directory.
  • Application DTO = UI/CLI input. Domain DTO = intra-module only.
  • Other modules never call Use Cases — only ACL / Events.

B6. Cross-module bridges (MUST)

Application/Domain of A never import Application/Domain of B.

NeedBridge
Peer answer nowSync ACL: local {Need}PortInterface → Infra ACL adapter → thin peer {This}ModuleInterface
Peer reacts laterAsync Domain Event → consumer Infra translation listener → inbound Use Case
  • Mapping only in ACL adapter; façades thin + HTTP-mappable; Prefer ACL validate before local commit.
  • Events: pure Domain class; Shared EventDispatcherInterface; eventId + occurredAt + schemaVersion + rich happy-path payload; idempotent consumers; no Domain re-fetch on happy path; prefer outbox.
  • No cross-module write transaction. Reports: ACL reads or owned projections.

B7. Shared promote rule (MUST)

SituationWhere
Tech used by one moduleThat module’s Infrastructure
Same tech used by ≥2 modulesPromote to Shared
Cross-module business conceptNever Shared — ACL / Events

Typical Shared: DomainException, DatabaseTransactionInterface, EventDispatcherInterface, ClockInterface, ConfigReaderInterface, Ensure.

B8–B9. Persistence & UI (MUST)

  • Map persistence records ↔ Domain Entities in Infrastructure; never leak ORM into Use Cases/Domain.
  • UI: authorize → DTO → Use Case → present.

B10. Decision tree

Only this module? → Golden flow
Need peer now? → ACL
Peer later? → Event
Multi-step? → Orchestration (still ACL+Events)
Report across contexts? → Projection / ACL read
Tech in 2+ modules? → Shared
Business in 2+ modules? → ACL/Events — never Shared

B11. Anti-patterns

Peer imports in Use Cases; god façades; thin-ID events; Shared business enums; ORM in Domain; Entity extends ORM; literal Feature/; cross-module DB TX; ORM joins across modules for policy.

B12. Examples

Ordering, Warehouse, Directory, Identity only — rename to your domains.

B13. Quality assertions

Boundary tests must fail CI if: foreign Application/Domain imports; peer *ModuleInterface in Use Cases; ORM/facades in Domain/Use Cases; literal Feature/ folders. Also: behaviour tests, formatter, static analysis, event idempotency.


Part C — Spiral host mapping

TargetSpiral Framework 3.17.x (+ RoadRunner; PHP ≥ 8.1)
Docshttps://spiral.dev/docs · Cycle bridge
Adapter HTML<ORIGIN>/v1/adapters/spiral

Spiral is long-living PHP with RoadRunner — hexagonal boundaries prevent request leakage across workers.

CoreSpiral place
Module rootapp/src/{Module}/ (or project convention)
SharedShared namespace
Composition rootBootloaders
PersistenceCycle ORM via spiral/cycle-bridge in Infrastructure
AsyncRoadRunner queues / jobs → Infra → Use Case
UIControllers / actions

See /v1/adapters/spiral/*.


Part D — Apply procedure (Spiral)

  1. Confirm Spiral 3.x + cycle-bridge
  2. README one-shot
  3. ROADMAP / PRD
  4. .ai ARCHITECTURE copy
  5. Bootloaders bind ports
  6. Cycle schema/migrations under Infra
  7. Queue consumers as Infra handlers
  8. Part E — no commit unless asked

Part F — Sketch

Controller → Use Case → ACL; RoadRunner job in Warehouse Infra deducts stock.


Part G — Done checklist (standard)


Part E — Assistant files (optional)

Skip if AI vendor is none.

Create shared folder:

.ai/modular-hexagonal-ddd/
  ARCHITECTURE.full.md   # copy of the host one-shot you fetched
  RULES.core.md
  SKILL.md
  CHECKLIST.md
  spiral-host.md         # host-only notes from Part C

E0 — RULES.core.md

# Modular Hexagonal DDD — Core rules
Canon: <ORIGIN>/v1/core (1.0.0-draft)
One-shot: <ORIGIN>/apply/spiral.full.md
Local: .ai/modular-hexagonal-ddd/ARCHITECTURE.full.md

1. Domain hardest: no host framework, ORM, other modules.
2. Use Cases: own Domain + Shared ports only.
3. Cross-module: ACL or Domain Events only.
4. Thin façades; rich events (eventId, schemaVersion); idempotent consumers.
5. No cross-module write TX; ACL validate-before-commit.
6. Shared = technical ≥2 modules only.
7. *UseCase + __invoke; no literal Feature/ folders.
8. Design test: peer→HTTP, Application/Domain still compile.

E1 — SKILL.md

---
name: modular-hexagonal-ddd
description: Enforce Modular Hexagonal DDD. Modules, ACL, Events, Shared, Use Cases.
---
Follow .ai/modular-hexagonal-ddd/ARCHITECTURE.full.md

E2 — CHECKLIST.md

- [ ] First-level *UseCase entry
- [ ] App DTO → __invoke
- [ ] I/O via Domain ports
- [ ] No ORM/facades in Application/Domain
- [ ] Sync peer → local ACL port → thin façade
- [ ] Async → rich event + translation listener → inbound UC
- [ ] Idempotent eventId
- [ ] No cross-module DB TX
- [ ] No foreign Application/Domain imports
- [ ] Shared only technical ≥2
- [ ] No Feature/ directories
- [ ] Replaceability test = yes

E3 — Cursor

  • .cursor/rules/modular-hexagonal-ddd.mdc ← E0 + path to ARCHITECTURE.full.md
  • .cursor/rules/spiral-host.mdc ← Part C summary
  • .cursor/skills/modular-hexagonal-ddd/SKILL.md ← E1

E4 — Claude

  • CLAUDE.md ← pointer to ARCHITECTURE.full.md + E0
  • Shared .ai/modular-hexagonal-ddd/*

E5 — Gemini

  • GEMINI.md or .gemini/instructions.md ← pointer + E0
  • Shared .ai/*

E6 — GitHub Copilot

  • .github/copilot-instructions.md ← E0 + short host section

E7 — Generic

  • AGENTS.md pointing at .ai/modular-hexagonal-ddd/ARCHITECTURE.full.md

E8 — Multiple vendors

One shared .ai/modular-hexagonal-ddd/; vendor entry files only point to it.

Modular Hexagonal Domain-Driven Design
Core 1.0.0-draft