ApiDomainer powers a robot battle league: send two lists of robot IDs, watch the platform assemble teams automatically, run a dance-off, and record the champion. The end result shows how to keep real-world business logic clean and testable while still shipping features quickly.
- Robot Dance-Off Engine – POST two squads of robot IDs and the domain layer forms the teams, validates participants, runs the match-up, and persists the winner.
- Clean CQRS Messaging – Read/write concerns split across Symfony Messenger buses; queries return optimized payloads while commands orchestrate domain workflows.
- View-Backed Read Models – Doctrine maps read-only projections via attributes (
config/packages/doctrine.yaml’sAppView) so we can hydrate DTO-like objects straight from SQL views. - API Platform Ready – Resources, filters, and responders wire the domain to HTTP without leaking framework details into the core logic.
- Developer-Friendly Separation – Domain, Application, Infrastructure, and Action layers keep responsibilities sharp and testing approachable.
A friendly API for running a robot dance-off league: send two sets of robot IDs, the platform assembles the teams, runs the matchup, and tracks the winner. Perfect for showcasing clean API flows to newcomers or recruiters without diving into internals.
- Start a new dance-off between two squads of robots.
- Replay an existing battle with a couple of roster swaps.
- Browse all robots and their stats.
- Check a scoreboard of recent battles and winners.
POST /api/robots/dance-off– Start a fresh battle with two lists of robot IDs.POST /api/robot-battles/replays– Replay a past battle with up to two swaps per side.GET /api/robot-battles– Scoreboard of battles and their latest results.GET /api/robots– Browse robots, filter, and sort.GET /api/robots/{id}– View a single robot.
make build / make up
make build
make install
make upVisit the 🌐 API endpoint
- Domain – Entities (
Robot,Team,RobotDanceOff,RobotBattle), repositories, and services such asRobotService, plus value objects likeDanceOffTeamsthat keep the domain framework-agnostic. - Application – Query handlers (e.g.,
GetRobotDanceOffQueryHandler) and orchestration logic that coordinate domain services via Symfony Messenger. - Infrastructure – Doctrine repositories, query builders, API Platform filters, and request DTOs. Handlers translate transport objects into domain value objects before delegating.
- Action / Responder – ADR-style actions act as controllers and responders turn domain models into API responses.
- Write-side commands persist canonical aggregates (
RobotDanceOff,Team,Robot). - Read-side queries hydrate
RobotBattleViewobjects from therobot_battle_viewSQL view; Doctrine treats the view namespace (App\\Domain\\ReadModel) as attribute-mapped entities declared read-only. - API Platform filters and orderers target these projections, keeping HTTP responses decoupled from the write models while still using Doctrine’s metadata and hydration pipeline.
- Request –
POST /api/robots/dance-offaccepts aRobotDanceOffRequestwith two arrays of robot IDs; the domain spins up a brand-new battle aggregate automatically. - Handler –
RobotDanceOffHandlerconverts the request into theDanceOffTeamsvalue object. - Domain Service –
RobotServicevalidates each robot, assembles twoTeamaggregates, runs the experience-based scoring algorithm, and persists the resultingRobotDanceOff. - Persistence & Response – Teams and dance-off entities are saved via Doctrine repositories. The responder layer presents a structured payload when queried.
POST /api/robot-battles/replays– Provide abattleIdplus up to two robot swaps per side (out➜in) to run another dance-off using the same battle history. Example payload:{ "battleId": 3, "teamAReplacements": [{ "out": 12, "in": 34 }], "teamBReplacements": [{ "out": 21, "in": 55 }] }GET /api/robot-battles/{battleId}/dance-offs– Retrieve the full list of dance-offs for a given battle, including every roster permutation and winner.GET /api/robot-battles/{battleId}/teams– Return only the robot ID lists for each team across every dance-off in the battle; perfect for building replay lineups or roster views.GET /api/robot-battles– Quick scoreboard summarising each battle’s latest result and total number of replays so you can surface standings at a glance.
GET /api/robots– Browse all registered robots, filter by name, sort by experience, or inspect their stats individually.GET /api/robots/dance-offs– List dance-offs with search and ordering support, including the parentbattleIdso you can branch into replays.GET /api/robots/{id}– Fetch a single competitor and see if they are ready for the next matchup.
Each read endpoint rides the query bus for separation of concerns and returns serialized responses through dedicated responders.
- PHP 8.2, Symfony Messenger, API Platform, Doctrine ORM
- Docker + Makefile for repeatable environments, Apache reverse proxy fronting the PHP-FPM container
- PHPUnit with lightweight stubs for fast feedback
This project demonstrates how production-grade patterns (DDD, CQRS, ADR) can stay approachable. You can trace a feature end-to-end—from HTTP request through domain logic—without wading through framework noise. Then visit http://localhost:8085/api


