go-ddd icon indicating copy to clipboard operation
go-ddd copied to clipboard

Don't return validated entities

Open maranqz opened this issue 10 months ago • 2 comments

Hello, thank you for you go-ddd template.

I want to clarify why did you propose this statements as the best practice?

  • Don't return validated entities from read methods in the repository. Instead, return the domain entity type directly.
    • Validations might change in the future, and you don't want to change all the data in your database.
    • Otherwise, you won't be able to read data from the database that was written with a different validation

I never see this approach before either Blue Book of Eric Evans (repository example) or Red Book of Vaughn Vernon (repository example).

I see some problems with your approach, that developers should call validation in many places, and they could easy forget to validate entity.

maranqz avatar Feb 16 '25 19:02 maranqz

Hi @maranqz

I never see this approach before either Blue Book of Eric Evans (repository example) or Red Book of Vaughn Vernon (repository example).

This is why it is called an "Opinionated Template" :)

I see some problems with your approach, that developers should call validation in many places, and they could easy forget to validate entity.

The trade-off here is between safety (always validating) and stability of your read‐model over time. If you force every read to go through your NewValidated… factory, then:

  • Any change to your validation rules (e.g. you tighten “name” constraints) will instantly break all existing rows in your database.
  • You’ll get errors just loading data that was perfectly fine under the old rules—now every “get” turns into “fix your data or update your code.”

Reads should be simple and future-proof (think of changing DB columns, changing business logic, etc.). You don't want to run a whole database migration every time to ensure the data is consistent and will pass all validation rules to avoid blow ups at runtime later.

By returning the plain domain entity on reads, you:

  • Guarantee you can always load historical data, regardless of how your validation logic has evolved.
  • Push validation to the write side—creation (NewX) and update methods—where you must enforce invariants anyway.

Yes, it means that whenever you take a loaded entity and intend to use it in a context that demands “this thing is valid,” you have to call your factory or validator at that moment. But that’s exactly what you want: Writes and business‐rule entry points become the single, obvious places where invariants are enforced. Reads stay read-only and never unexpectedly crash because of rule changes.

sklinkert avatar Apr 28 '25 05:04 sklinkert

You can create a factory without validation FromDB without validation and allow the calling it only in the repository by linter to resolve an old invalid data problem.

My main concern is the separation of change and entity verification, creating a temporal/sequential relationship. Uniting changes and validation sounds simpler than remembering to call the validation entity after any changes.

maranqz avatar May 24 '25 11:05 maranqz