LibDomain
A Domain-Driven Design framework for Go enabling type-safe, event-sourced aggregates through declarative definitions and code generation.

What is LibDomain?
LibDomain is a complete framework for building complex business systems using Domain-Driven Design (DDD) principles in Go. It provides:
- Declarative Domain Modeling - Define domain structure once, generate everything else
- Event Sourcing - Complete audit trail and temporal queries built-in
- Type Safety - Compile-time guarantees from definitions to code
- Code Generation - Automatic boilerplate generation (~80% reduction)
- Lazy Loading - Explicit property loading prevents N+1 queries
- Multi-Tenancy - First-class tenant isolation support
Key Statistics
- 6,000+ lines of core library code
- 80,000+ lines of generated code (across all domains)
- 20+ property types supported
- 15 architecture decisions documented
- 6 comprehensive guides for developers
Quick Start
1. Define Your Domain
// myapp/domain/theme/definitions.go
package theme
import (
"libdomain"
"libdomain/properties"
)
func init() {
defs := &libdomain.Definitions{
Package: "myapp/domain/theme",
Repository: "myapp/domains/theme",
}
defs.Register(&libdomain.AggregateDefinition{
AggregateRoot: &libdomain.EntityDefinition{
Name: "theme",
Properties: []libdomain.PropertyDefinition{
properties.Text("title", true),
properties.Text("code", true),
properties.Integer("sequence", true),
},
},
})
libdomain.Generate(defs)
}
2. Implement Domain Logic
// myapp/domain/theme/theme.go
package theme
func (t *Theme) UpdateTitle(ctx context.Context, newTitle string) error {
if len(newTitle) < 3 {
return errors.New("title too short")
}
event := &ThemeTitleUpdatedEvent{
Title: newTitle,
}
t.Apply(event)
return nil
}
3. Wire Application Layer
// myapp/application/theme/wire.go
package theme
import themegen "myapp/domain/theme/gen"
func init() {
// Wire validation
themegen.ThemeValidation = NewThemeValidator()
// Wire authorization
themegen.ThemeAuthPolicy = NewAuthorizationPolicy()
// Wire event handlers
themegen.ThemeEventHandler = NewEventHandler()
}
4. Use in Services
// myapp/application/theme/create_usecase.go
func (u *CreateThemeUseCase) Handle(ctx context.Context, cmd CreateThemeCommand) error {
// Create aggregate
theme, err := u.factory.NewTheme(ctx, cmd.TenantID, cmd.Title, cmd.Code, cmd.Sequence)
if err != nil {
return err
}
// Save to repository
return u.repo.Save(ctx, theme)
}
Documentation
LibDomain includes comprehensive documentation for different audiences:
For Architects & Decision Makers
For Developers
For Contributors
Reading Order
New to LibDomain?
- ARCHITECTURE.md - Understand the system
- CONCEPTS.md - Learn terminology
- ERROR_HANDLING.md - Learn error philosophy
Ready to contribute?
4. CONTRIBUTING.md - Code standards and process
5. EXTENDING.md - Extension patterns
Deep dives:
Core Concepts
Aggregate
A cluster of domain objects (root entity, child entities, value objects) treated as a single unit.
Theme Aggregate:
├── Aggregate Root: Theme
│ ├── Properties: title, code, sequence
│ └── Methods: UpdateTitle, UpdateExternal
├── Child Entities: ThemeProjectItem[]
└── Value Objects: ThemeExternal{}
Event Sourcing
Store immutable domain events instead of current state. Rebuild state by replaying events.
User Action → Command → Event Created → Event Stored → Aggregate Replayed
Domain Events
Immutable facts about what happened in the domain.
type ThemeCreatedEvent struct {
Title string
Code string
Sequence int
OccurredAt time.Time
OccurredBy string
}
Lazy Loading
Properties must be explicitly loaded before access. Prevents N+1 queries.
if !theme.TitleIsLoaded() {
theme, err = repo.FindByID(ctx, themeID)
}
title := theme.Title() // Safe
Type-Safe Repository
Per-aggregate repository interface for data access.
type ThemeRepository interface {
Save(ctx context.Context, theme *Theme) error
FindByID(ctx context.Context, id ThemeID) (*Theme, error)
FindByCode(ctx context.Context, code string) (*Theme, error)
}
Architecture Overview
┌─────────────────────────────────────────┐
│ Presentation Layer │ HTTP/gRPC/WebSocket
│ (API Handlers, DTOs, Serialization) │
├─────────────────────────────────────────┤
│ Application Layer │ Use Cases, Commands
│ (Services, Event Handlers, Commands) │ Queries, Transactions
├─────────────────────────────────────────┤
│ Domain Layer (✓ LibDomain) │ Aggregates, Events
│ (Entities, Value Objects, Events) │ Repositories, Policies
├─────────────────────────────────────────┤
│ Infrastructure Layer │ Persistence
│ (Repositories, Event Store, DB) │ Event Publishing
└─────────────────────────────────────────┘
Error Handling Philosophy
LibDomain distinguishes between two types of errors:
Panic: Programmer Errors
// Invariant violation - programmer error
if !t.titleIsLoaded {
panic("BUG: Title accessed before loading. Call AllPropertiesLoaded() first.")
}
// Initialization failure - programmer error
if ThemeEventHandler == nil {
panic("BUG: ThemeEventHandler not initialized in init()")
}
Why? Returning errors for programmer errors leads to silent data corruption.
Error: Runtime/Operational Issues
// User input validation - operational
if len(title) < 3 {
return NewValidationError("title", "must be at least 3 characters")
}
// Business rule violation - operational
if !hasRole(ctx, "admin") {
return NewAuthorizationError("admin role required")
}
// External system failure - operational
db, err := sql.Open("postgres", connStr)
if err != nil {
return fmt.Errorf("database connection failed: %w", err)
}
Why? Users can fix input errors, system can retry external calls, but programmer errors must be fixed in code.
See ERROR_HANDLING.md for complete philosophy.
Extension Points
LibDomain provides clean extension points for custom behavior:
Custom Property Types
Create new property types beyond the 20+ built-in types.
properties.CustomType("metadata", false, map[string]string{
"description": "Custom metadata storage",
})
Custom Validation Rules
Implement domain-specific validation.
type ThemeValidator struct { /* ... */ }
func (v *ThemeValidator) ValidateTitle(title string) error {
// Custom validation logic
}
Custom Authorization Policies
Control who can perform which actions.
type RoleBasedAuthPolicy struct { /* ... */ }
func (p *RoleBasedAuthPolicy) AuthorizeThemeCreate(ctx, event) (bool, error) {
// Authorization logic
}
Custom Event Handlers
React to domain events with side effects.
type CustomEventHandler struct { /* ... */ }
func (h *CustomEventHandler) OnThemeCreated(setters, event) {
// Event handling logic
}
Custom Repository Functions
Add custom query methods.
type ThemeRepository interface {
// Generated
FindByID(ctx context.Context, id ThemeID) (*Theme, error)
// Custom
FindByCode(ctx context.Context, code string) (*Theme, error)
}
See EXTENDING.md for complete extension guide.
Design Principles
1. Single Source of Truth
Domain structure defined once in definitions, generated everywhere.
2. Type Safety
Compile-time guarantees from definitions to code.
3. Explicit Dependencies
Properties must be explicitly loaded before access.
4. Invariant Protection
Business rules enforced at aggregate level.
5. Event-First
State changes recorded as events first, then applied.
6. Auditability
All changes recorded in event log.
7. Testability
Each component has clear interface, mockable for testing.
Supported Property Types
| Category |
Types |
| Scalar |
Text, Integer, Boolean, Float |
| Temporal |
Date, DateTime |
| References |
ID, Entity, Slice |
| Complex |
ValueObject, OrderedMap, Selection |
| Special |
Binary, Translatable |
Each property supports:
- Required/optional constraints
- Length constraints (min/max)
- Pattern validation (regex)
- Lazy loading
- Custom validation
Project Status
Phase 1: Architecture Documentation ✅ COMPLETE
- ARCHITECTURE.md - System design and components
- DESIGN_DECISIONS.md - 15 ADRs explaining design
- CONCEPTS.md - DDD terminology reference
- ERROR_HANDLING.md - Error philosophy guide
- EXTENDING.md - Extension guide
- CONTRIBUTING.md - Contribution guidelines
Phase 2: Refactor Core Types (Planned)
- Split definition.go (6,000+ lines) into 9 focused files
- Add comprehensive godoc comments
- Improve type organization
- Create property helper functions
Phase 3-7: Code Quality & Release (Planned)
- Generator refactoring
- Interface-based injection
- Comprehensive testing
- Performance optimization
- Open-source release preparation
See LIBDOMAIN_REFACTORING_PLAN.md for complete roadmap.
Building
# Download dependencies
go mod download
# Run tests
go test ./...
# Check code quality
go vet ./...
go fmt ./...
# Build examples
go build ./examples/...
Contributing
LibDomain welcomes contributions from the community!
Before contributing, please read:
- CONTRIBUTING.md - Contribution guidelines
- ARCHITECTURE.md - System design
- CONCEPTS.md - DDD terminology
Contribution Process:
- Fork the repository
- Create a feature branch
- Follow coding standards and testing requirements
- Submit pull request with clear description
- Address review feedback
- Merge when approved
See CONTRIBUTING.md for detailed guidelines.
Architecture Decisions
LibDomain is built on well-reasoned architectural decisions documented in DESIGN_DECISIONS.md.
Key decisions include:
- ADR-003: Panic for programmer errors, error return for runtime issues
- ADR-002: Event sourcing as core pattern
- ADR-004: Lazy loading of properties
- ADR-008: Interfaces for all major components
See DESIGN_DECISIONS.md for all 15 ADRs.
Examples
See examples/ directory for complete working examples:
examples/
└── theme/ # Complete theme domain example
├── definitions.go # Domain definitions
├── domain/ # Domain logic
├── application/ # Application services
└── tests/ # Example tests
License
LibDomain is proprietary software. See LICENSE file for details.
Support
For questions or issues:
- Check ARCHITECTURE.md for system design
- Check CONCEPTS.md for terminology
- Check ERROR_HANDLING.md for error guidance
- See EXTENDING.md for extension patterns
- Read CONTRIBUTING.md for code standards
For bug reports and feature requests, please use the issue tracker.
Roadmap
Phase 1: Architecture Documentation ✅ COMPLETE
- Professional documentation for open-source quality
Phase 2: Core Library Refactoring (Next)
- Split monolithic definitions into focused modules
- Improve type organization
Phase 3-7: Quality & Release
- Generator improvements
- Comprehensive testing
- Performance optimization
- Open-source release
See PHASE_1_SUMMARY.md for detailed timeline.
Architecture at a Glance
Definitions (DSL)
↓
Generator (Templates)
↓
Generated Code (Types, Events, Handlers)
↓
Domain Logic (Aggregates, Services)
↓
Application Services (Use Cases, Commands)
↓
Presentation (APIs, DTOs)
↓
Infrastructure (Repositories, Event Store)
Data Flow:
User Action
↓
Command
↓
Event Created
↓
Event Stored
↓
Aggregate Replayed
↓
State Consistent
Key Statistics
| Metric |
Value |
| Go Version |
1.21+ |
| Core Library |
~6,000 lines |
| Generated Code |
80,000+ lines |
| Documentation |
12,000+ words |
| Code Examples |
150+ |
| Architecture Decisions |
15 |
| Property Types |
20+ |
| Extension Points |
5+ |
Next Steps
For Users:
- Read ARCHITECTURE.md
- Review examples in
examples/
- Check CONCEPTS.md for terminology
- See EXTENDING.md for customization
For Contributors:
- Read CONTRIBUTING.md
- Review DESIGN_DECISIONS.md
- Check coding standards in CONTRIBUTING.md
- See EXTENDING.md for patterns
For Architects:
- Review ARCHITECTURE.md
- Study DESIGN_DECISIONS.md
- Check PHASE_1_SUMMARY.md for roadmap
LibDomain: Domain-Driven Design for Go 🚀
Built with principles of event sourcing, type safety, and maintainability.