Application Service
A component that orchestrates use cases by coordinating domain objects, repositories, and external services.
An application service is the place where your use cases live. It sits between the entry points (HTTP handlers, CLI commands, message subscribers) and your domain logic, orchestrating the steps needed to fulfill a request.
A typical application service method follows a pattern: load data from a repository, call some domain logic, save the result, and maybe notify an external system. It's a coordinator. It doesn't contain business rules itself, unless the domain is trivial and doesn't need a dedicated domain layer.
In Go, an application service is usually a struct that takes repository interfaces and other dependencies through its constructor:
type Service struct {
orders OrderRepository
payments PaymentService
}
func (s Service) PlaceOrder(ctx context.Context, cmd PlaceOrderCommand) error {
order, err := NewOrder(cmd.CustomerID, cmd.Items)
if err != nil {
return err
}
if err := s.orders.Save(ctx, order); err != nil {
return err
}
return s.payments.InitializePayment(ctx, order.ID(), order.TotalPrice())
}
The service depends on interfaces, not implementations. It doesn't know if the repository talks to PostgreSQL or an in-memory store. It doesn't know if the payment service calls Stripe or a mock. This is the Dependency Inversion Principle in practice.
As the application grows, a single service struct can become large. If you use the same data model for all operations, it's easy to couple the methods too much. CQRS addresses this by splitting it into separate command and query handlers, each focused on one use case. The idea is the same: orchestrate without owning the business rules.
Cross-cutting concerns like logging, metrics, and tracing fit naturally as decorators or middleware around your application service methods. This keeps the orchestration code focused on the use case itself.
References
- How to implement Clean Architecture in Go (Golang) — Introduces the application service as a struct in the app package that serves as the entry point to application logic. Shows how to extract business logic from HTTP handlers into an application service that depends on repository interfaces.
- How to use basic CQRS in Go — Shows how CQRS splits a single application service into separate command and query handlers. Demonstrates refactoring from a monolithic TrainingService to focused handlers, each responsible for one use case.
- Combining DDD, CQRS, and Clean Architecture in Go — Covers the application layer's role in orchestrating domain logic. Shows that if domain-related if statements appear in the application layer, they should be moved to the domain layer.
- When using Microservices or Modular Monolith in Go can be just a detail? — Demonstrates that the application layer stays the same whether using microservices or a monolith. Shows an application service orchestrating order placement by coordinating product lookup, order creation, and payment initialization.
- Microservices test architecture. Can you sleep well without end-to-end tests? — Discusses testing the application layer. Notes that application service code often glues other layers together, and testing it adds value only when there's complex orchestration in commands.
- Increasing Cohesion in Go with Generic Decorators — Describes the application layer split into commands and queries, CQRS style. Shows how generic decorators wrap application service handlers to add cross-cutting concerns like logging and metrics.
- Common Anti-Patterns in Go Web Applications — Advocates dedicating a separate application layer to your product's most important code. Shows how the application layer uses its own models and mappings to stay independent of HTTP, gRPC, or any other entry point.
- Repository secure by design: how to sleep better without fear of security vulnerabilities — Discusses trade-offs of placing filtering logic in the application layer versus the repository. Shows that some operations are too expensive to do at the application service level and belong in optimized database queries.
- Is Clean Architecture Overengineering? — Discusses the application layer's role in Clean Architecture and whether separating it from domain and infrastructure adds unnecessary complexity. Covers when application services make sense and when they become overengineering.
- AMA #1: Clean Architecture, Learning, Event-Driven, Go — Community Q&A covering application layer topics. Discusses where logic belongs (domain vs application layer), how to structure application services, and practical advice for keeping layers clean.
- Synchronous vs Asynchronous Architecture — Mentions application services in the context of CQRS and security. Suggests using multiple small command-based services instead of large application services to get basic secure design for free.