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.

ITPUB
ITPUB
ITPUB
When Should You Use Domain Services in Go? A Practical DDD Guide

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backend ArchitectureGoDDDsoftware designDomain-Driven DesignDomain Services
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.