Services
Services
After you complete the domain models, you can start building the services. The following describe a service:
- It performs a significant piece of business logic within the domain.
- It calculates some values.
- It interacts with the repository layer.
Try to push down as much logic as possible into your domain objects (i.e. create service functions within the domain .go
files.)
Domain services provide services within the domain, while application services are used to compose other services and repositories–they do not contain domain logic.
Example
A student wants to register for a class. You have the Student
entity:
type Student struct {
ID int
firstName string
lastName string
courses []Class
}
And you have the Class
entity:
type Class struct {
ID int
Name string
students []Student
maxEnrollment int
}
Next, we want to register a student for the class. You might consider creating a Student.AddClass
or Class.AddStudent
method, but either of those methods would require that you reference another entity and perform business logic. For example:
// This is bad
func (s *Student) AddClass(c *Class) error {
// validation logic
if len(c.students) > c.maxEnrollment {
return errors.New("class is full")
}
// ...
// Class business logic
c.append(c.students, s)
return nil
}
The solution is to add a RegistrationService
. First, define the struct:
// RegistrationService registers students for the class.
type RegistrationService struct {
class *Class
}
Next, create a constructor that injects a Class instance into the service:
func NewRegistrationService(c *Class) *RegistrationService {
return &RegistrationService{class: c}
}
Finally, define the RegisterStudent
method on the RegistrationService
:
func (r *RegistrationService) RegisterStudent(s *Student) error {
if len(r.class.students) > r.class.maxEnrollment {
return errors.New("class is full")
}
if r.class.contains(s) {
return errors.New("student is already enrolled in this class")
}
r.class.students = append(r.class.students, *s)
return nil
}