Friday, April 25, 2025
HomeGolangWhat's the widespread strategy to reconstruct a site mannequin e.g. from a...

What’s the widespread strategy to reconstruct a site mannequin e.g. from a database mannequin? – Getting Assist


Given a site mannequin for a simplified todo app

sort 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
}

// different getters...

func (t *Todo) Rename(newTitle string) error {
    if t.isMarkedAsDone {
        return errors.New("todo is already marked as accomplished")
    }

    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 accomplished")
    }

    t.isMarkedAsDone = true
    t.modifiedAt = time.Now()

    return nil
}

// different setters...

Saving this todo to a retailer isn’t any drawback since I can entry the fields by way of getters. However once I question the shop for todos which are marked as accomplished I can’t reconstruct a site object from the returned information.

The constructor operate takes no isMarkedAsDone = true parameter ( + isMarkedAsDone ) and if I’d attempt to create a brand new area todo and name the MarkAsDone operate on it I’d overwrite the sector modifiedAt which is incorrect then.

What’s a standard strategy to unravel this?

  • Make the whole lot public? ( feels incorrect to me, customers may put the area object in invalid state )
  • Change the entire constructor operate to simply accept all fields and validate all of them, so customers have to supply all fields from outdoors?
  • Preserve the whole lot as is however present a reconstruct operate in the identical package deal accepting all fields ( + validation ) that creates a site mannequin and has write entry to the personal fields because it lives in the identical package deal?

The idiom for this kinf of drawback in Go is use optionally available Sample. Please test

And in some instances i aldo apply Builder sample

Thanks in your reply. However how would you deal with the modifiedAt subject? Having a WithModifiedAt-function?

Usually you’d by no means have such a operate as a result of this subject shall be set on each modification. So if you happen to go for the choices sample you might additionally setup one large operate that validates each parameter, no?

Sure, it doesn’t want a getter/setter and create an possibility operate per subject

True, however when calling a operate like Rename or MarkAsDone on Todo you’d nonetheless replace the modifiedAt subject, proper?

What if customers then name WithModifiedAt once more on it? I personally assume they shouldn’t have the ability to take action …

That is the most suitable choice imo.

Making all fields public violates encapsulation and accepting all fields in constructor can result in misusing it.

Hello Joachim,

I observed this query is a bit older, however I wished to supply some suggestions on the code. Please take this as a pleasant suggestion to make your code extra approachable.

It looks like many individuals code on this model, and I’m curious why that’s. :thinking: Maybe it’s seen as a intelligent strategy to code, however generally simplicity could be more practical and simpler to grasp for everybody.

I wish to present some recommendation that may assist others who come throughout this thread as properly.

As an alternative of specializing in a standard strategy, why not purpose for a very good strategy? A website mannequin may not be obligatory, and also you don’t want constructors on this case. You may simply initialize the app struct.

Right here’s my suggestion:

As an alternative of utilizing features straight in your Todo, think about using the features in your database connector. Use your db.Fashions straight as an alternative of redefining them. (Assuming you’re utilizing SQL to retailer todos persistently.)?

Right here’s some pseudocode as an example:

sort App struct {
    Queries *db.Queries
}

func (app *App) getTodo(todoId uuid.UUID) (todo db.Todo, err error) {
    todo, err := app.Queries.GetTodo(context.TODO(), db.GetTodoParams{ID: todoId})
    // error dealing with
    return todo, err
}

func (app *App) createTodo(title string) (err error) {
    _, err := app.Queries.CreateTodo(context.TODO(), db.CreateTodoParams{Title: title})
    // error dealing with
    return err 
}

Attempt to keep away from pressured OOP. Because you’re engaged on todo lists, it’s an awesome alternative to observe. By implementing each approaches, you’ll have the ability to see which one works higher. Usually, the easier answer proves to be more practical, and real-life duties will introduce sufficient complexity on their very own.

Go is straightforward.

I hope this helps. Have a beautiful weekend! :sun_with_face:

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments