Domain Service vs Application Service in DDD
Fast recall
Quick revision
Core answer
TransferService.transfer(fromAccount, toAccount, amount)). It's pure domain code, framework-free. An Application Service (= Use Case / Interactor) is a thin orchestrator that coordinates one user goal: load aggregates from repositories, call domain logic, save, publish events, manage transactions. Domain services know the domain; application services know the use case. Application services should contain no business rules; domain services should contain no infrastructure concerns.Distinguishing orchestration from domain logic, when to use a domain service (multi-aggregate logic, no natural home), and avoiding 'service-class' anti-patterns where everything ends up in services.
Putting business rules in application services (which become 1000-line god classes), or making everything a domain service even when it belongs on an entity.
Discuss the test 'does this logic belong on an entity? if not, why?' before reaching for a domain service; relationship to Hexagonal/Clean's use cases.
Interview-ready guide
Interview answer
Two different 'services'
DDD uses 'service' for two very different things.
Domain service: lives in domain layer. Holds domain logic that doesn't fit on any single entity or value object. Typically across multiple aggregates. Stateless; pure domain code; no Spring, JPA, HTTP. Named in domain language — PricingPolicy, FundsTransferService, RouteCalculator.
Application service (= use case / interactor): lives in application layer. Orchestrates one user goal end-to-end. Loads aggregates from repositories, invokes domain logic, saves, publishes events. Owns transaction boundary. Knows about infrastructure concerns but not their implementation. Named after the goal — PlaceOrderUseCase, CancelSubscriptionUseCase.
Rule of thumb: if it knows about repositories or transactions, it's an application service. If it just operates on domain objects, it's a domain service — or it should be on an entity.
When to use a domain service
Test 1: could this method live on an entity? If yes, put it on the entity. order.cancel() belongs on Order, not OrderCancellationService.
Test 2: does it operate across multiple aggregates? Yes → candidate for domain service. transfer(fromAccount, toAccount, amount) touches two Account aggregates.
Test 3: domain rule/policy with no natural home? TaxCalculator with jurisdiction lookups, PricingPolicy varying by tier and season. Domain logic but not bound to a specific entity.
If none of the above, you don't need a domain service. Most logic on entities or value objects.
Domain service example
FundsTransferService.transfer(from, to, amount) checks from.canDebit(amount), calls from.debit(amount), to.credit(amount). No @Service, no Spring, no @Transactional, no repository calls. Operates on already-loaded aggregates.
Application service is what loads/saves: TransferFundsUseCase pulls Account aggregates from repository, calls transferService.transfer(from, to, amount), saves both. @Transactional on use case.
Clean split: use case handles I/O and transactions; domain service handles the domain rule.
Application service — thin orchestration
Application services are thin. Their job is plumbing:
- 1Pull aggregates via repositories
- 2Delegate to entities / value objects / domain services
- 3Persist the result
- 4Publish domain events
- 5Manage transaction boundary
PlaceOrderUseCase.execute(cmd): construct Order, reserve inventory, order.submit(), orderRepo.save(order), events.publishAll(order.drainEvents()), return order.id().
No business logic in this method. Every business decision (cancel, submit, addItem) lives in Order or a domain service. The use case is a script: load, decide via domain, save, emit.
The anti-pattern: logic in application services
Developers default to Spring @Service and pour business logic in. OrderService.cancel(id) loads order, checks if SHIPPED, sets CANCELLED, saves. Now business rule duplicated in every cancel flow (refund, admin, scheduled). Order is anemic; invalid state reachable via setStatus from anywhere. Fat service classes accumulate dozens of methods.
Fix: push rule into entity. Order.cancel() checks status, mutates, raises event. CancelOrderUseCase loads, calls o.cancel(), saves, publishes events.
Common mistakes
- Calling everything 'a service' — OrderService doing cancellation rules + HTTP→DB mixes layers
- Domain services for everything — most logic belongs on entities
- Application services with business logic — anemic entities + fat services = classic anti-pattern
- Domain services with @Transactional or repositories — they should know about domain objects only
- One huge OrderService — split by use case
- Domain service that's actually a calculator — fine as static method on value object
- Confusing 'domain service' with Spring @Service — stereotype doesn't tell you which kind
Recommendation
Default to putting logic on entities and value objects. Reach for a domain service only when the logic genuinely spans multiple aggregates or has no natural home. Application services (use cases) are thin orchestrators — one per user goal — that load aggregates, delegate to domain logic, save, and publish events. Application services contain no business rules; domain services contain no infrastructure concerns. Spring's @Service annotation tells you nothing about layer; the package and dependencies do.
Domain Service vs Application Service in DDD
Direct Answer
A Domain Service holds domain logic that doesn't naturally fit in any single entity or value object — typically operations spanning multiple aggregates (e.g., TransferService.transfer(fromAccount, toAccount, amount)). It's pure domain code, framework-free. An Application Service (= Use Case / Interactor) is a thin orchestrator that coordinates one user goal: load aggregates from repositories, call domain logic, save, publish events, manage transactions. Domain services know the domain; application services know the use case. Application services should contain no business rules; domain services should contain no infrastructure concerns.
Common Mistakes & Pitfalls
Putting business rules in application services (which become 1000-line god classes), or making everything a domain service even when it belongs on an entity.
Check your understanding
Answer these without looking back at the article.
- Show how funds-transfer logic should be split between an entity, domain service, and application service.
- Why is putting business rules in @Service classes the classic anemic-domain anti-pattern?
- Should a domain service depend on a repository?
- When would `TaxCalculator` be a static method on `Money` instead of a domain service?
Related interview prep categories
1Want a guided curriculum order?
These modules are also sequenced in our flagship backend interview track — open it when you prefer an ordered path instead of browsing by topic.