Use Cases & Application Services in NestJS DDD


In modern backend architecture, especially with Domain-Driven Design (DDD), we’re often told to keep our domain pure and our services slim. But how do you actually orchestrate your domain logic? This is where Use Cases and Application Services step in.
Use cases are not optional fluff — they are the entry points into your domain logic. Whether triggered by an API, an event, or a CLI command, they coordinate how your app responds to real-world actions.
“Use cases describe what the system does, not how it does it.”
Why We Need Use Cases
Let’s say we’re building a user registration flow. You could just throw everything into a UserService
, but then you end up with bloated service classes mixing validation, orchestration, and domain logic.
With use cases:
Logic is separated from controllers and services
You make dependencies explicit
Each use case is single-responsibility and testable
Application Services (like UserService
) become thin layers that delegate to these use cases, or are sometimes replaced entirely by them in a DDD setup.
💡 Use cases are often implemented as standalone service classes — for example,
register-user.service.ts
. This naming is completely valid and aligns well with NestJS conventions. The key is their focus: a single responsibility coordinating domain logic.
The Role of Application Services
In NestJS, we often default to services for everything. But not all services are created equal. In DDD:
Domain Services contain domain-specific logic
Application Services orchestrate domain interactions
Think of Application Services as coordinators: they handle security, transactions, and trigger domain behaviors — but they themselves are not the business logic.
Structuring Use Cases in NestJS
Instead of bloated user.service.ts
files, create dedicated use case classes:
src/
└── user/
├── use-cases/
│ ├── register-user.service.ts
│ └── update-user-profile.service.ts
Each use case class should:
Be injectable
Depend on repositories, not ORM or DB
Call domain methods (entities, value objects)
Return meaningful results
Less Code, More Intention
Rather than showing a giant code snippet, here's what matters:
Use cases define your language. They're named like "RegisterUser", "CreateOrder", or "SendInvoice" — not just "service".
They accept input DTOs and return results, keeping boundaries explicit.
You can test them without NestJS. That’s clean design.
What About CQRS?
Use cases also align naturally with Command Query Responsibility Segregation (CQRS):
Commands → Use Cases (mutate state)
Queries → Query Handlers (fetch data)
This helps keep read/write models clean and optimized for their responsibilities.
Quick Recap
✅ Use cases isolate application logic and orchestrate domain actions
✅ Application Services delegate responsibilities and handle cross-cutting concerns
✅ You avoid bloated services and keep your domain clean
✅ They enable testability, maintainability, and intent-driven design
Up Next
In the next article, we’ll explore Domain Services — where shared domain logic goes when it doesn't fit neatly into a single Entity or Value Object.
What’s Your Take?
Are you organizing your app logic with use cases? Or still trying to fit it all in the service layer?
Let us know — tag @codanyks and share your thoughts!
Subscribe to my newsletter
Read articles from codanyks directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
