Given a domain model for a simplified todo app
type Todo struct {
title string
isMarkedAsDone bool
modifiedAt time.Time
}
func NewTodo(title string) (*Todo, error) {
if title == "" {
return nil, errors.New("title is empty")
}
todo := &Todo{
title: title,
isMarkedAsDone: false,
modifiedAt: time.Now(),
}
return todo, nil
}
func (t *Todo) GetTitle() string {
return t.title
}
func (t *Todo) IsMarkedAsDone() bool {
return t.isMarkedAsDone
}
// other getters...
func (t *Todo) Rename(newTitle string) error {
if t.isMarkedAsDone {
return errors.New("todo is already marked as done")
}
if newTitle == "" {
return errors.New("new title is empty")
}
t.title = newTitle
t.modifiedAt = time.Now()
return nil
}
func (t *Todo) MarkAsDone() error {
if t.isMarkedAsDone {
return errors.New("todo is already marked as done")
}
t.isMarkedAsDone = true
t.modifiedAt = time.Now()
return nil
}
// other setters...
Saving this todo to a store is no problem since I can access the fields via getters. But when I query the store for todos that are marked as done I can’t reconstruct a domain object from the returned data.
The constructor function takes no isMarkedAsDone = true
parameter ( + isMarkedAsDone
) and if I would try to create a new domain todo and call the MarkAsDone
function on it I would overwrite the field modifiedAt
which is wrong then.
What is a common approach to solve this?
- Make everything public? ( feels wrong to me, consumers could put the domain object in invalid state )
- Change the whole constructor function to accept all fields and validate all of them, so consumers have to provide all fields from outside?
- Keep everything as is but provide a
reconstruct
function in the same package accepting all fields ( + validation ) that creates a domain model and has write access to the private fields since it lives in the same package?