When Should You Use Domain Services in Go? A Practical DDD Guide
This article explains the role of domain services in Go‑based Domain‑Driven Design, outlines three situations where they are appropriate, shows how to implement them statelessly, recommends functional package organization, and demonstrates dependency inversion with interfaces and infrastructure implementations.
Building on previous posts about value objects and entities in Go, this guide focuses on domain services—an essential DDD concept for encapsulating logic that does not naturally belong to any single entity or value object.
In some cases, the clearest and most practical design includes operations that conceptually do not belong to any object; rather than forcing them into a category, introduce a new element called Service.
Following the single‑responsibility principle, you should only create a domain service when the required logic cannot be placed in an entity or value object without violating that principle.
When a Domain Service Is Needed
Entity Requires Repository Access – If a method inside an entity needs to call a repository (e.g., counting effective product evaluations), move that logic to a domain service to keep the entity pure.
Interaction Between Multiple Entities – When business rules involve coordination between several entities (such as validating a product review that depends on both product and order contexts), encapsulate the coordination in a domain service.
Logic Cannot Fit Any Entity – Scenarios like user login, where the operation does not belong to a specific entity, should be handled by a domain service.
Each case is illustrated with diagrams (see images below) that show why placing the code in an entity would create unwanted dependencies or circular references.
Organizing Domain Service Code
Rather than grouping code by business domain (e.g., domain.product, domain.order), the article recommends a functional package layout: place all domain services in a dedicated domain.service package. This avoids circular dependencies and keeps the codebase manageable as services grow.
Keeping Services Stateless
Domain services should not hold state such as request contexts or user IDs. The article contrasts two approaches:
Defining a struct with fields like ctx or userId (shown in an image) – this couples the service to a specific session and is discouraged.
Using plain functions that receive required value objects and repositories as parameters – this is stateless but can become cumbersome when many dependencies are needed.
The preferred solution is to define a service struct that only contains interfaces for external resources (e.g., repositories, external APIs). These interfaces are defined in the domain layer, implemented in the infrastructure layer, and injected at application startup, achieving dependency inversion.
For example, a SpamChecker interface is declared in the domain, while an AiSpamChecker implementation lives in infra.sal. The domain service depends on the interface, not the concrete implementation, allowing easy swapping of technology without affecting business logic.
Implementation Example
The article includes several images that depict the struct definitions and function signatures for both the naïve and the improved approaches, highlighting how to inject dependencies and keep the service stateless.
Conclusion
The guide summarizes that domain services are valuable for encapsulating cross‑entity or infrastructure‑related logic, but they should be used sparingly. If a piece of logic can reside in an entity or value object, prefer that placement. The next article will explore factories in DDD.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
