skip to content

Application vs Domain Services

Application services are thin: they open transactions, check authorization, map DTOs and call the domain. Domain services hold business rules. Interviewers ask because the most common design failure is business logic drifting up into the application layer.

part ofSoftware design & architectureoverview, primer and where to startread it →
on this pageshow

questions

6

In a layered application built with Domain-Driven Design, what is the basic difference between an 'application service' and a 'domain service'?

level: juniorimportance: must knowfreq 70%

answer

  1. orchestrator vs rule-holder
  2. transaction boundary = application service
  3. spans multiple aggregates = domain service candidate
  4. DTOs stay in application layer
  5. domain service = stateless, pure

basics

~20 s

An application service coordinates a use case — starts transactions, checks permissions, calls objects, converts data — without deciding business rules. A domain service holds business rules that don't fit one entity, like a rule spanning two accounts.

solid answer

~40 s

Application services orchestrate a use case at the domain layer's boundary: open a transaction, enforce authorization, load aggregates via repositories, delegate real work to domain objects, then map the result to a DTO. They hold no business rules themselves. Domain services live inside the domain layer and encapsulate business logic that doesn't naturally belong to one entity or value object — typically because it spans multiple aggregates or represents a process-like concept (e.g., transferring money between two accounts) rather than a property of a single object. The application service's job is orchestration; the domain service's job is business logic that lacks a single natural owner among the entities involved.

go deeper

for a junior

Should articulate the basic distinction (orchestration vs business rules) and give one example of each without needing to defend edge cases.

for a middle

Should explain why application services own transactions/security/DTO mapping and be able to say a domain service should be stateless and infrastructure-free.

for a senior

Should recognize when an operation belongs on an aggregate vs needs to become a domain service (multi-aggregate span, process-like concept), and name the anemic-domain-model risk of getting it wrong.

for a principal

Should discuss this as an organizational/architectural concern: how misplacing logic compounds across a codebase, how to review PRs for this smell, and how to set team conventions that prevent transaction-script sprawl.

## The layers in play To understand the split, start with the layers a typical DDD application uses: - an **interface layer** (controllers) - an **application layer** (application services) - a **domain layer** (entities, value objects, aggregates, domain services, domain events) - an **infrastructure layer** (repository implementations, external clients) Each layer has one job, and the application-vs-domain-service question is really about which of two adjacent layers a given piece of code belongs in. ## The application service is a use-case orchestrator An application service is a use-case orchestrator. When a controller receives a request like "place order #123," it doesn't talk to the database or run business rules itself — it calls an application service method, e.g. `placeOrder(orderId, customerId)`. That method's body is a short, procedural script: 1. begin a transaction 2. check the caller is authorized to place orders for that customer 3. load the `Customer` and `Cart` aggregates via their repositories 4. ask the domain to do the actual work 5. save the result 6. publish any domain events 7. translate the return value into a response DTO None of these steps encode business rules — they encode **process**: what has to happen, in what order, for this use case to run safely. The application service is intentionally **thin**: if you find yourself writing an if-statement that decides whether an order is allowed to ship based on inventory levels, that's business logic and it has leaked into the wrong layer. ## The domain service holds rules with no single owner A domain service, by contrast, lives inside the domain layer and holds business logic — but only the kind that doesn't belong to a single entity or value object. Most business rules should sit directly on the aggregate they concern: an `Order` aggregate should have a method like `order.addLine(product, quantity)` that enforces its own invariants (e.g., can't add lines after checkout). But some operations genuinely span multiple aggregates, or represent a domain concept that reads as a process rather than a property of one object. - **A classic example** is transferring money between two `Account` aggregates: the debit and credit have to be coordinated, and neither account "owns" the transfer — so a `MoneyTransferService` domain service exists to hold that rule. - **Another common case** is anything requiring an external policy that isn't naturally a method of one entity, like calculating shipping cost based on a `PricingPolicy` that varies by season, region, and customer tier — the policy is business logic, but it doesn't belong to any single `Order` instance, so it becomes `ShippingCostCalculator`, a domain service, injected with whatever value objects it needs and returning a value object as its result. ## Why the split matters operationally The two matter operationally because of **transactions** and **testability**. - **Application services are the transaction boundary.** They own the transactional annotation or equivalent, they know about security context, and they're where DTO mapping happens because DTOs are an application-layer concern (the domain layer should never know about a JSON representation of itself). - **Domain services, meanwhile, should be pure with respect to infrastructure:** no direct SQL, no HTTP calls, no knowledge of the current user or request. They take domain objects in and domain objects out, which makes them trivially unit-testable — you construct value objects and aggregates in memory and assert on the returned value, no database or mocking framework needed. If a "domain service" method signature includes a database connection, an HTTP client, or a raw security-context object, that's a strong sign it's actually infrastructure/application code wearing a domain-service name tag. ## Getting it wrong, in both directions The trade-off of getting the split wrong runs both ways. | Failure direction | What it produces | |---|---| | Push too much into the application service | You get a transaction script — long procedural methods full of if-statements checking business rules, duplicated across every use case that touches the same aggregate, because there's no single owner for the rule. This is the classic symptom of an **anemic domain model**: entities become plain data bags (getters/setters only) and all the intelligence lives in services, which defeats the purpose of modeling the domain at all. | | Push too much into domain services | You get **"service-itis"** — a domain service for every single operation, even ones that clearly belong to one entity, which fragments behavior away from the data it operates on and makes the model harder to reason about; callers now have to know which of a dozen domain services to call for a given entity instead of just calling a method on the entity. | ## A concrete example A concrete real-world example: in an e-commerce system, `CheckoutApplicationService.checkout(cartId)` is the application service — it: - starts a transaction - loads the cart - calls `cart.finalize()` (an aggregate method enforcing "cart must have at least one item") - calls a `PriceCalculator` domain service to apply promotions that depend on both the cart and an external `PromotionCatalog` - saves the resulting `Order` aggregate - returns an `OrderConfirmationDto` The rule "a cart needs one item to be finalized" lives on the aggregate; the rule "promotions combine using this precedence" lives in the domain service, because it isn't the responsibility of any single aggregate.

  • Where should DTO-to-domain-object mapping code live, and why?
    In the application service (or a mapper it calls), not in the domain layer. The domain model should have no knowledge of the wire format used by any particular client — a REST DTO is an application/interface-layer concern. If mapping code creeps into entities or domain services, the domain layer picks up a dependency on transport concerns, making it harder to reuse the same domain logic behind a different API later.
  • Can a domain service call a repository directly?
    It's discouraged in the strict tactical-DDD reading, since repositories are usually invoked by the application service. Many practical codebases do inject repositories into domain services when an operation genuinely needs to look up related aggregates mid-calculation, but that blurs the line, so teams that do this should keep repository calls read-only and keep persistence (saves) exclusively in the application service.
  • Is it ever correct to have zero domain services in a well-modeled bounded context?
    Yes — many bounded contexts are simple enough that every business rule fits naturally on an aggregate or value object, and introducing a domain service for the sake of it would just add indirection. Domain services should be the exception used when an operation truly doesn't belong to one entity, not a default landing spot.

Think of a restaurant: the application service is the waiter — takes the order, coordinates with the kitchen and the register, doesn't decide the recipe. The domain service is like a specialist chef brought in only for a dish that needs cross-station coordination (a shared dessert course), while most dishes are cooked entirely by one station (the entity) using its own recipe.

saying these in an interview costs you the question

  • Says domain services and application services are basically interchangeable / just naming
  • Puts transaction management or authorization checks on a domain service
  • Can't name a concrete reason an operation needs a domain service instead of an entity method
  • Thinks DTO mapping belongs in the domain layer
  • Treats 'domain service' as a synonym for any class ending in *Service

context

open as a page

In a DDD-layered backend, an application service method is often described as 'transaction, security, orchestration, DTO mapping — but no business rules.' What does each of those four responsibilities concretely mean in code, and why must business rules stay out of this layer?

level: middleimportance: must knowfreq 75%

basics

~20 s

An application service starts the transaction, checks the caller is authorized, calls domain objects in order, and converts results to an API shape — but never decides business rules; those live in the domain objects it calls.

open as a page

A codebase's OrderApplicationService.placeOrder method has grown over two years to include inventory checks, tax calculation, fraud scoring, and shipping rule logic, all as inline conditionals, while the Order entity itself only has getters and setters. What is this anti-pattern called, why does it happen incrementally, and how would you refactor it?

level: seniorimportance: must knowfreq 65%

basics

~20 s

This is the anemic domain model anti-pattern — entities hold no behavior, logic piles into services. It happens because adding one more 'if' is easier than redesigning the entity. Fix: move each rule to the object owning its data.

open as a page

How should unit-testing strategy differ between a domain service (like a pricing calculator that applies business rules) and an application service (like a use-case orchestrator that opens a transaction and calls repositories) in a DDD-layered backend?

level: middleimportance: should knowfreq 55%

basics

~20 s

A domain service can be tested with plain objects — call it and check the result. An application service usually needs mocked or fake repositories to test, since its job is coordinating things, not doing the business math itself.

open as a page

You're modeling a 'FundsTransfer' operation between two Account aggregates in a banking domain. Walk through the reasoning for why this operation should become a domain service rather than a method on the Account entity, and what the domain service's method signature and statelessness constraints should look like.

level: seniorimportance: should knowfreq 55%

basics

~20 s

Neither Account owns a transfer between two accounts, so it doesn't fit as one entity's method. A stateless domain service takes both accounts and the amount, applies debit/credit rules, and returns the results — holding no data between calls.

open as a page

In a system where placing an order should raise an OrderPlaced domain event, should the domain logic that decides to raise that event publish it directly to an event bus/message broker itself, or should it merely return/collect the event for the application service to publish after the transaction commits? Explain the reasoning.

level: principalimportance: nice to knowfreq 30%

basics

~20 s

The business logic should just record that an event happened — not broadcast it. The application service should send it, and only after the database changes are safely committed, so nobody hears about something that later gets rolled back.

open as a page