Molders Service Kind
Overview
Molders are form-specific services responsible for data transformation between form data structures and domain model entities. They provide bidirectional mapping, change detection, and field-level transformation logic for different form operations like creation and updates.
Architecture
Core Components
Form-Specific Molders (src/srv/molders/)
- Each form operation has its own dedicated molder (e.g., CreateGardenFormMolder, UpdateGardenFormMolder)
- Molders are organized by operation type and entity
- No base library - molders implement simple, focused transformation logic
Integration Points
- Forms (src/dat/forms/): Source data structures from user input
- Models (src/mdl/): Target domain entities
- Handlers: Consumers of molder transformation results
Naming Convention
Molders follow a consistent naming pattern:
- Create{Entity}FormMolder: For entity creation forms
- Update{Entity}FormMolder: For entity update forms
- Additional operation-specific molders as needed
Standard Methods
Every molder implements these standard transformation methods:
Form-to-Entity Transformation
ToEntity(form, entity) (*Entity, []string)
- Transforms form data into domain model entity
- Returns the modified entity and a list of changed field names
- Handles validation and type conversion during transformation
Entity-to-Form Transformation
ToForm(form, entity) *Form
- Populates form structure with data from domain entity
- Used for pre-filling forms during edit operations
- Ensures form reflects current entity state
Usage Examples
Creating Entities from Forms
// Transform form data to entity
entity := &mdl.Garden{}
garden, changes := createGardenMolder.ToEntity(form, entity)
// Changes list can be used for selective updates
fmt.Printf("Modified fields: %v", changes)
Pre-filling Edit Forms
// Load existing entity data into form
form := &forms.UpdateGardenForm{}
populatedForm := updateGardenMolder.ToForm(form, existingGarden)
Update Operations with Change Detection
// Transform update form, detecting changes
entity := existingGarden // Load existing entity
updatedEntity, changes := updateGardenMolder.ToEntity(form, entity)
// Use changes list for optimized updates
if len(changes) > 0 {
handler.UpdateFields(ctx, updatedEntity, changes, nil)
}
Change Detection
Update molders implement change detection logic:
Simple Field Comparison
func (this *UpdateGardenFormMolder) ToEntity(form *forms.UpdateGardenForm, entity *mdl.Garden) (*mdl.Garden, []string) {
var changes []string
if entity.Name != form.Name {
entity.Name = form.Name
changes = append(changes, "Name")
}
if entity.Picture != form.Picture {
entity.Picture = form.Picture
changes = append(changes, "Picture")
}
return entity, changes
}
Change List Usage
The returned changes list enables: - Optimized Updates: Only update modified fields in database - Audit Logging: Track which fields were changed - Validation: Apply field-specific validation rules - Event Triggers: Fire events for specific field changes
Form Types
Create Forms
- Contain only fields needed for entity creation
- No ID fields (generated by system)
- All required fields for entity initialization
- Example:
CreateGardenForm{Name, Picture}
Update Forms
- Include entity ID for targeting specific records
- Contain all editable fields
- Support partial updates through change detection
- Example:
UpdateGardenForm{Id, Name, Picture}
Data Flow
Creation Flow
- User submits creation form
- Binder extracts form data from request
- Form Validator validates input data
- Molder transforms form to entity
- Handler persists entity to database
Update Flow
- User submits update form
- Binder extracts form data from request
- Form Validator validates input data
- Fetcher retrieves existing entity
- Molder transforms form to entity with change detection
- Handler updates only changed fields
Integration with Framework
With Handlers
// Create operation
entity := &mdl.Garden{}
garden, _ := molder.ToEntity(form, entity)
createdGarden, err := handler.Create(ctx, garden, nil)
// Update operation
updatedGarden, changes := molder.ToEntity(form, existingGarden)
result, err := handler.UpdateFields(ctx, updatedGarden, changes, nil)
With Form Presenters
// Pre-fill form for editing
form := &forms.UpdateGardenForm{}
populatedForm := molder.ToForm(form, existingEntity)
presenter.Present(populatedForm)
Field Transformation Logic
Molders handle various transformation concerns: - Type Conversion: String to appropriate field types - Format Normalization: Standardizing input formats - Default Values: Setting defaults for missing fields - Field Mapping: Handling differences between form and entity field names
Error Handling
Molders typically don’t handle errors directly but rely on: - Form Validators: Pre-validate form data before molding - Type System: Go’s type system catches conversion errors - Business Logic: Downstream validation in handlers and entities