Start here
Quick Start Architecture Components Frameworks Contributing Coding StandardHexagonal Architecture and CQRS¶
Fight Common keeps business language at the center and makes frameworks replaceable at the edge. Source-code dependencies point inward: Domain code knows no framework; Application code coordinates the Domain through ports; Adapter code connects those ports to databases, transports, and frameworks.
The dependency rule¶
Framework composition, persistence, queues, HTTP clients, files, and provider integrations.
Replaceable edgeUse-case coordination, ports, buses, handlers, filters, subscribers, and transaction boundaries.
Stable boundaryBusiness values, rules, specifications, messages, collections, and repository vocabulary.
Protected centerThis direction is about ownership, not request flow. A web request may enter through Symfony, Laravel, Yii, CodeIgniter, Slim, or framework-free composition. The Adapter translates it into an Application operation; the Application coordinates Domain behavior; results travel back out without making the Domain depend on the caller.
src/Standards is deliberately outside this chain. It publishes development-time coding policy and has
no runtime dependents. Domain, Application, and Adapter code cannot use it, and it cannot use runtime code.
Ports and adapters¶
A port is a contract owned by the code that needs the capability. An adapter fulfills that contract using a particular technology. Framework bootstrapping chooses the adapter; portable Domain and Application code does not.
MailTransport
MailService sends an application message through this port without knowing which mailer exists.
SymfonyMailTransport
The adapter translates the portable message into Symfony Mailer calls and infrastructure failures.
Repositories use the same separation. Domain-owned Pagination and ResultSet express
business-facing query vocabulary. Application-owned TransactionalUnitOfWork marks the atomic use-case
boundary. The DoctrineTransactionalUnitOfWork adapter supplies the ORM-specific implementation. Your
application owns its aggregate repository port because only your application can define what loading and saving
that aggregate means.
Framework packages belong at the composition edge. Their service providers, compiler passes, and configuration bind Fight Common contracts to selected adapters. They may call inward; they do not move framework types into Domain or portable Application code.
CQRS message flows¶
CQRS separates requests to change state from requests to read it. Events then announce facts that have already happened. Fight Common keeps the message payloads in Domain, the coordination contracts in Application, and the routing or transport mechanisms in Adapter.
CommandCommandBus::execute()filters wrap dispatchCommandHandler::handle()coordinates mutationQueryQueryBus::fetch()filters wrap dispatchQueryHandler::handle()returns read dataEventEventDispatcher::trigger() registration maps event typesEventSubscriberindependent reactionsWhat each layer owns¶
- Domain owns
Command,Query, andEventpayload contracts plusCommandMessage,QueryMessage, andEventMessageenvelopes. Messages carry identity, metadata, timestamp, and the typed payload without knowing how they will be delivered. - Application owns
CommandBus,QueryBus,EventDispatcher, handlers, filters, and subscribers. Handlers coordinate a use case; filters wrap command or query dispatch for concerns such as validation or metrics; subscribers declare which event facts they react to. - Adapter owns routing, pipelines, service-container lookup, and transport. Routing command and query buses match one handler. Simple and service-aware event dispatchers invoke registered listeners. Messenger, Laravel, and CodeIgniter adapters can move supported commands and events through asynchronous infrastructure.
Queries are synchronous because fetch() returns a result. Commands and events can use synchronous or
asynchronous adapters, selected at composition time. Asynchronous delivery changes transport and failure timing;
it does not change which layer owns the message or its handler contract.
The Quick Start shows these parts in one order-processing path: a command handler uses
repository and payment ports, commits through TransactionalUnitOfWork, and triggers an event that a
subscriber turns into a follow-up command.
Repository enforcement¶
The architecture exists in the public contracts and dependency direction first. Deptrac is repository evidence that the implementation still matches that model.
The runtime configuration classifies every production class, rejects outward dependencies, and gives each layer an explicit external-package allowance. Domain remains framework-free. Application may use PHP internals, neutral PSR contracts, and the allowlisted scheduler expression contract. Adapter integrations and their third-party packages are named explicitly. A separate unassigned-token check fails if production or Standards code escapes classification. There is no baseline of accepted violations.
Deptrac is a Fight Common development dependency, not a runtime requirement for consumers. A consuming project can install it independently and define boundaries for its own namespaces; copying Fight Common’s package-specific configuration would not describe that project’s architecture.