Factory Pattern
A function or method that encapsulates the creation of domain objects, ensuring they start in a valid state.
A factory encapsulates the logic of creating domain objects so callers don't need to know the details. When creation logic gets complex or when the same object can be built from different sources, you move that logic into a dedicated place.
A factory is more than a constructor. It makes decisions like which variant to create, what defaults to apply, how to enforce invariants that span multiple fields. It uses external dependencies to generate IDs, fetch related data, or validate business rules.
The creation rules for a fresh object and for one loaded from the database are different. A new object goes through full validation, ID generation, and default setup. A persisted object can trust the stored values and skip those checks. Separate factory methods let you enforce each path cleanly.
type SessionFactory struct{
// dependencies like repositories, ID generators, etc.
}
// NewAvailableSession creates a fresh session. Full validation applies.
func (f SessionFactory) NewAvailableSession(hourTime time.Time) (*Session, error) { ... }
// UnmarshalSessionFromDatabase reconstructs a session from persisted data.
// It trusts the stored values and skips business validation.
func (f SessionFactory) UnmarshalSessionFromDatabase(hourTime time.Time, availability Availability) (*Session, error) { ... }
Factories pair naturally with aggregates. Aggregates often have complex creation logic involving multiple entities and invariants. They also pair with repositories: the repository loads rows, then asks the factory to turn them back into domain objects.
References
- The Repository pattern in Go: a painless way to simplify your service logic — Uses an hour.Factory to reconstruct domain objects when loading from different databases. The factory ensures hours are created with proper validation regardless of the storage backend.