service-layer
Skill4 starsChanged 4 months agoMITView on GitHub
Give each use case a thin application service that orchestrates domain objects and repositories, owns the transaction boundary, and exposes a clear entry point — keeping controllers thin and business rules out of the delivery layer. Use when the user wants one class per use case that coordinates domain objects and repositories to carry out an operation; the user wants to move business logic out of controllers so the delivery layer only translates requests and responses; the user wants a single place that begins and commits the transaction wrapping a whole use case. Keywords: service-layer, application-service, use-case, transaction, orchestration, boundary.
1 other project uses a version of this.
Adopted by
Most recently taken up by
- aditya-deokar/software-factorytheir own version, first seen Sep 29, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- aditya-deokar/software-factory★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository119 other files it gives its agents.
- strategy-bitrix-d7-incrementalSkill
- strategy-explicit-over-magicSkill
- strategy-framework-conventionsSkill
- strategy-legacy-safe-editsSkill
- strategy-minimal-diffSkill
- strategy-no-untested-refactorSkill
- strategy-preserve-public-apiSkill
- strategy-readonly-immutabilitySkill
- strategy-security-firstSkill
- strategy-test-drivenSkill
- openapiSkill
- paginationSkill
- rest-designSkill
- versioningSkill
- cqrsSkill
- dependency-inversionSkill
- hexagonalSkill
- repository-patternSkill
- bitrixSkill
- findings-reportSkill
- laravelSkill
- performanceSkill
- phpSkill
- project-analysisSkill
- refactoringSkill
- roi-prioritizationSkill
- securitySkill
- stack-detectionSkill
- cache-layersSkill
- component-engineSkill
- d7-eventsSkill
- d7-loaderSkill
- d7-orm-queriesSkill
- iblock-elementsSkill
- kernel-main-moduleSkill
- localizationSkill
- controllersSkill
- ormSkill
- validationSkill
- controllersSkill
- query-builderSkill
- routingSkill
- autoload-psr4Skill
- dependency-managementSkill
- scriptsSkill
- connection-poolingSkill
- mysql-indexingSkill
- redisSkill
- transactionsSkill
- ci-github-actionsSkill
- deployment-strategiesSkill
- docker-phpSkill
- env-configSkill
- associationsSkill
- entities-mappingSkill
- orm-basicsSkill
- entity-apiSkill
- module-developmentSkill
- routing-controllersSkill
- formsSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 3 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 66
Do not reach for a service layer as the place to put domain rules that belong on the entity or value object itself.
- Distinctive: line 67
Do not add an application service that only forwards a call straight to a repository with no coordination or transaction.
- Very specific: line 78
Create one application service for the use case with a plain input, e.g.
- Distinctive: line 125
Not applicable — no I/O, auth, or untrusted input.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
<!-- generated: do not edit — source: knowledge/atoms/architecture/service-layer/ --><!-- schema: atomVersion=3 skillVersion=2 graphVersion=1 --><!-- compile: @php-skills/compiler v1.0.0 (deterministic build) -->Service Layer (Application Services)
When to use
- The user wants one class per use case that coordinates domain objects and repositories to carry out an operation.
- The user wants to move business logic out of controllers so the delivery layer only translates requests and responses.
- The user wants a single place that begins and commits the transaction wrapping a whole use case.
When NOT to use
- Do not reach for a service layer as the place to put domain rules that belong on the entity or value object itself.
- Do not add an application service that only forwards a call straight to a repository with no coordination or transaction.
Core guidance
- Give each use case its own service method — Model each use case as one application service (or one method on a focused service) with a
clear name like
PlaceOrderorregisterUser. - Own the transaction at the service boundary — Begin and commit the transaction inside the application service so the whole use case is one atomic unit of work, rolling back on failure.
Quick recipe
- Create one application service for the use case with a plain input, e.g.
final class PlaceOrderwhoseexecute(PlaceOrderInput $input): OrderPlacednames the operation. (verify: The service takes a plain input object and no framework request) - In
execute(), open a transaction, load aggregates from injected repositories, call their behavior, persist them, then commit — rolling back on any failure. (verify: The whole use case commits atomically, and a thrown exception rolls it back) - Have the controller map the request to the input DTO, call the service, and turn its result into a response — with no business logic of its own. (verify: The controller only translates request to input and result to response)
Best practices
- Have the service accept a plain input object or scalars and return a plain result, not the framework request or an ORM entity, so it stays independent of the delivery mechanism
- Inject repositories and external gateways into the service as interfaces, so the use case is wired to abstractions and can be tested with fakes
- Let the service coordinate — load aggregates, call their behavior, save them — and push the actual rules down into the domain objects it uses
Anti-patterns
| Anti-pattern | Why it fails | Do instead |
| One god service for a whole module | A god service becomes a change magnet with tangled dependencies, and its methods share no cohesion beyond belonging to the same noun. | Split it into one focused application service per use case, each depending only on the<br>collaborators that use case needs. |
| Business logic living in the controller | Logic in the controller cannot be reused outside HTTP and mixes delivery concerns with the use case, so the service layer's purpose is lost. | Extract the orchestration and transaction into an application service the controller invokes<br>with a plain input object. |
| Returning the ORM entity from the service | Handing back a mutable ORM entity lets the delivery layer change persistent state and couples views to the database schema. | Return a plain result DTO with just the data the caller needs, mapping from the entity inside<br>the service. |
See references/anti-patterns.md.
Common mistakes
- The service swelling into a transaction script — Letting all the business rules pile up inside the service method produces a long procedural transaction script with an anemic domain underneath. Fix: Move each rule onto the entity or value object it belongs to, leaving the service to load, invoke behavior, and save.
- Framework request objects leaking into the service — Passing the raw HTTP request or session into the application service ties the use case to the web delivery mechanism. Fix: Have the controller extract the needed values into a plain input DTO and pass that to the service, keeping the service delivery-agnostic.
- Nested services with tangled transactions — Having one application service call another, each opening its own transaction, produces nested or overlapping transaction scopes that are hard to reason about. Fix: Keep one transaction boundary per top-level use case; extract shared steps into domain services or plain collaborators that do not open their own transaction.
Version notes
- Immutable input DTOs with PHP 8.1 readonly (PHP >=8.1) — PHP 8.1 readonly promoted properties make the service's input and result DTOs immutable in a couple of lines, so the use case receives a fixed request.
- Property hooks tidy result DTOs in PHP 8.4 (PHP >=8.4) — From PHP 8.4, property hooks let a result DTO expose a computed field (a formatted total) without a separate getter, keeping the service's output object small.
Security
- Not applicable — no I/O, auth, or untrusted input.
Testing
- Activation and decision behavior are verified in
eval/scenarios/architecture/service-layer.yaml. - Inject repositories and external gateways into the service as interfaces, so the use case is wired to abstractions and can be tested with fakes.
Additional resources
- references/anti-patterns.md
- references/core.md
- references/examples.md
- references/pitfalls.md
- references/recipes.md
- references/version-differences.md
Knowledge graph
Prerequisites: architecture/dependency-inversion
Related: architecture/cqrs, architecture/repository-pattern
<!-- graph-fragment: graph/fragments/architecture/service-layer.yaml -->Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

