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?
Thanks for your reply. But how would you handle the modifiedAt field? Having a WithModifiedAt-function?
Normally you would never have such a function because this field will be set on every modification. So if you go for the options pattern you could also setup one big function that validates every parameter, no?
I noticed this question is a bit older, but I wanted to offer some feedback on the code. Please take this as a friendly suggestion to make your code more approachable.
It seems like many people code in this style, and I’m curious why that is. Perhaps it’s seen as a clever way to code, but sometimes simplicity can be more effective and easier to understand for everyone.
I want to provide some advice that might help others who come across this thread as well.
Instead of focusing on a common approach, why not aim for a good approach? A domain model might not be necessary, and you don’t need constructors in this case. You can just initialize the app struct.
Here’s my suggestion:
Instead of using functions directly on your Todo, consider using the functions on your database connector. Use your db.Models directly instead of redefining them. (Assuming you are using SQL to store todos persistently.)?
Try to avoid forced OOP. Since you’re working on todo lists, it’s a great opportunity to practice. By implementing both approaches, you’ll be able to see which one works better. Often, the simpler solution proves to be more effective, and real-life tasks will introduce enough complexity on their own.