Maestro Pattern
Overview
The Maestro Pattern is the core business logic orchestration pattern in this framework. It provides a structured approach to implementing complex business operations through sequential step execution with clear input/output contracts.
Pattern Principles
Single Responsibility
Each Mae (Maestro module) handles one specific business operation:
- CreateGardenMae
- Creates a new garden
- UpdateUserProfileMae
- Updates user profile information
- ProcessOrderMae
- Handles order processing workflow
Sequential Execution
Mae modules execute business logic in defined sequential steps: 1. Validation - Input validation and business rule checking 2. Preparation - Data transformation and preparation 3. Persistence - Database operations and state changes 4. Post-Processing - Notifications, logging, cleanup 5. Response - Result formatting and return
Explicit Contracts
Mae modules define clear input and output contracts:
type CreateGardenMaeInput struct {
Form *forms.CreateGardenForm
UserId string
}
type CreateGardenMaeOutput struct {
Garden *mdl.Garden
Success bool
}
Mae Structure
Basic Mae Template
package mae
type CreateGardenMae struct {
// Service dependencies
GardenFetcher *fetchers.GardenFetcher
GardenHandler *handlers.GardenHandler
GardenMolder *molders.CreateGardenFormMolder
UserFetcher *fetchers.UserFetcher
// Utility dependencies
Logger *logger.Logger
Time time.Time
}
func (this *CreateGardenMae) Execute(ctx context.Context, input *maes.CreateGardenMaeInput) (*maes.CreateGardenMaeOutput, error) {
// Step 1: Validate input
if err := this.validateInput(input); err != nil {
return nil, err
}
// Step 2: Check business rules
if err := this.checkBusinessRules(ctx, input); err != nil {
return nil, err
}
// Step 3: Transform data
garden := this.prepareGarden(input)
// Step 4: Persist to database
createdGarden, err := this.GardenHandler.Create(ctx, garden, nil)
if err != nil {
return nil, err
}
// Step 5: Post-processing
this.Logger.Info("Garden created successfully", "garden_id", createdGarden.Id)
return &maes.CreateGardenMaeOutput{
Garden: createdGarden,
Success: true,
}, nil
}
Step Methods
Mae modules break down execution into focused step methods:
func (this *CreateGardenMae) validateInput(input *maes.CreateGardenMaeInput) error {
if input.Form == nil {
return erro.N("Form is required")
}
if input.Form.Name == "" {
return erro.N("Garden name is required")
}
if input.UserId == "" {
return erro.N("User ID is required")
}
return nil
}
func (this *CreateGardenMae) checkBusinessRules(ctx context.Context, input *maes.CreateGardenMaeInput) error {
// Check if user exists
user, exists, err := this.UserFetcher.FindOneById(ctx, input.UserId, nil)
if err != nil {
return err
}
if !exists {
return erro.N("User not found")
}
// Check user's garden limit
mod := this.GardenFetcher.Mod()
mod.ExactStringValueFilter("user_id", input.UserId)
gardens, err := this.GardenFetcher.FindAll(ctx, mod)
if err != nil {
return err
}
if len(gardens) >= user.MaxGardens {
return erro.N("User has reached maximum garden limit")
}
return nil
}
func (this *CreateGardenMae) prepareGarden(input *maes.CreateGardenMaeInput) *mdl.Garden {
garden := &mdl.Garden{}
garden, _ = this.GardenMolder.ToEntity(input.Form, garden)
garden.UserId = input.UserId
garden.CreatedAt = this.Time.Now()
garden.UpdatedAt = this.Time.Now()
return garden
}
Mae Input/Output Structures
Input Structure Pattern
// src/dat/maes/create_garden_mae.go
package maes
type CreateGardenMaeInput struct {
Form *forms.CreateGardenForm // User input
UserId string // Context data
Options *CreateGardenOptions // Operation options (optional)
}
type CreateGardenOptions struct {
SkipValidation bool
SendEmail bool
CreateDefault bool
}
Output Structure Pattern
type CreateGardenMaeOutput struct {
Garden *mdl.Garden // Primary result
Success bool // Operation success flag
Messages []string // User messages
Metadata map[string]interface{} // Additional data
}
Advanced Mae Patterns
Transaction Management
func (this *CreateGardenWithPlantsMae) Execute(ctx context.Context, input *maes.CreateGardenWithPlantsMaeInput) (*maes.CreateGardenWithPlantsMaeOutput, error) {
// Begin transaction
tx, err := this.SqlDb.Begin(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback() // Will be no-op if committed
// Step 1: Create garden
handlerMod := &handler.HandlerMod{Tx: tx}
garden, err := this.GardenHandler.Create(ctx, input.Garden, handlerMod)
if err != nil {
return nil, err
}
// Step 2: Create plants
for _, plantForm := range input.PlantForms {
plant := this.preparePlant(plantForm, garden.Id)
_, err := this.PlantHandler.Create(ctx, plant, handlerMod)
if err != nil {
return nil, err
}
}
// Commit transaction
if err := tx.Commit(); err != nil {
return nil, err
}
return &maes.CreateGardenWithPlantsMaeOutput{
Garden: garden,
Success: true,
}, nil
}
Cross-Entity Operations
func (this *TransferGardenOwnershipMae) Execute(ctx context.Context, input *maes.TransferGardenOwnershipMaeInput) (*maes.TransferGardenOwnershipMaeOutput, error) {
// Step 1: Validate both users exist
currentOwner, err := this.validateUser(ctx, input.CurrentOwnerId)
if err != nil {
return nil, err
}
newOwner, err := this.validateUser(ctx, input.NewOwnerId)
if err != nil {
return nil, err
}
// Step 2: Validate garden ownership
garden, err := this.validateGardenOwnership(ctx, input.GardenId, input.CurrentOwnerId)
if err != nil {
return nil, err
}
// Step 3: Check new owner capacity
if err := this.checkOwnerCapacity(ctx, input.NewOwnerId); err != nil {
return nil, err
}
// Step 4: Transfer ownership
garden.UserId = input.NewOwnerId
updatedGarden, err := this.GardenHandler.Update(ctx, garden, nil)
if err != nil {
return nil, err
}
// Step 5: Update related entities
if err := this.updateRelatedEntities(ctx, garden.Id, input.NewOwnerId); err != nil {
return nil, err
}
// Step 6: Send notifications
this.sendTransferNotifications(ctx, currentOwner, newOwner, garden)
return &maes.TransferGardenOwnershipMaeOutput{
Garden: updatedGarden,
PreviousOwner: currentOwner,
NewOwner: newOwner,
Success: true,
}, nil
}
Mae Composition
func (this *CompleteGardenSetupMae) Execute(ctx context.Context, input *maes.CompleteGardenSetupMaeInput) (*maes.CompleteGardenSetupMaeOutput, error) {
// Step 1: Create garden
createGardenOutput, err := this.CreateGardenMae.Execute(ctx, &maes.CreateGardenMaeInput{
Form: input.GardenForm,
UserId: input.UserId,
})
if err != nil {
return nil, err
}
// Step 2: Add default plants
for _, plantForm := range input.DefaultPlants {
plantForm.GardenId = createGardenOutput.Garden.Id
_, err := this.CreatePlantMae.Execute(ctx, &maes.CreatePlantMaeInput{
Form: plantForm,
})
if err != nil {
// Log error but continue
this.Logger.Error("Failed to create default plant", "error", err)
}
}
// Step 3: Setup garden tasks
if err := this.setupDefaultTasks(ctx, createGardenOutput.Garden.Id); err != nil {
this.Logger.Error("Failed to setup default tasks", "error", err)
}
return &maes.CompleteGardenSetupMaeOutput{
Garden: createGardenOutput.Garden,
Success: true,
}, nil
}
Error Handling in Maes
Structured Error Response
type MaeError struct {
Code string `json:"code"`
Message string `json:"message"`
Field string `json:"field,omitempty"`
Context map[string]interface{} `json:"context,omitempty"`
}
func (this *CreateGardenMae) validateBusinessRules(ctx context.Context, input *maes.CreateGardenMaeInput) error {
// Check garden name uniqueness
existing, _, err := this.GardenFetcher.FindByName(ctx, input.Form.Name, nil)
if err != nil {
return &MaeError{
Code: "database_error",
Message: "Failed to check garden name uniqueness",
Context: map[string]interface{}{
"operation": "find_by_name",
"name": input.Form.Name,
},
}
}
if existing != nil {
return &MaeError{
Code: "validation_error",
Message: "Garden name already exists",
Field: "name",
Context: map[string]interface{}{
"existing_id": existing.Id,
},
}
}
return nil
}
Mae Testing Patterns
Unit Testing Structure
func TestCreateGardenMae_Success(t *testing.T) {
// Arrange
ctx := context.Background()
mae := setupCreateGardenMae(t)
input := &maes.CreateGardenMaeInput{
Form: &forms.CreateGardenForm{
Name: "Test Garden",
Description: "A test garden",
},
UserId: "user123",
}
// Act
output, err := mae.Execute(ctx, input)
// Assert
require.NoError(t, err)
assert.True(t, output.Success)
assert.Equal(t, "Test Garden", output.Garden.Name)
assert.NotEmpty(t, output.Garden.Id)
}
func TestCreateGardenMae_ValidationError(t *testing.T) {
// Arrange
mae := setupCreateGardenMae(t)
input := &maes.CreateGardenMaeInput{
Form: &forms.CreateGardenForm{
Name: "", // Invalid: empty name
},
UserId: "user123",
}
// Act
output, err := mae.Execute(context.Background(), input)
// Assert
assert.Error(t, err)
assert.Nil(t, output)
assert.Contains(t, err.Error(), "name is required")
}
Integration Testing
func TestCreateGardenMae_Integration(t *testing.T) {
// Setup real database and services
db := setupTestDatabase(t)
container := setupTestContainer(t, db)
mae := container.CreateGardenMae
// Test complete flow
input := createValidInput()
output, err := mae.Execute(context.Background(), input)
// Verify database state
garden := mustFindGardenInDb(t, db, output.Garden.Id)
assert.Equal(t, input.Form.Name, garden.Name)
}
Mae Best Practices
Design Guidelines
- Single Business Operation - Each Mae handles one coherent business operation
- Clear Steps - Break complex operations into focused step methods
- Input Validation - Always validate input early and comprehensively
- Error Context - Provide detailed error context for debugging
- Transaction Boundaries - Use transactions for multi-entity operations
Implementation Guidelines
- Dependency Injection - Inject all required services through constructor
- Context Propagation - Pass context through all service calls
- Logging - Log significant operations and errors
- Testing - Write both unit and integration tests
- Documentation - Document business rules and edge cases
Performance Considerations
- Batch Operations - Use batch operations when processing multiple entities
- Lazy Loading - Only load data that’s actually needed
- Connection Management - Proper database connection handling
- Memory Management - Avoid holding large datasets in memory
LLM Mae Development Notes
When implementing Mae modules: - Start with input/output structure definition - Break complex operations into sequential steps - Always validate input and business rules - Use explicit error types with context - Test both success and failure scenarios - Follow transaction patterns for data consistency - Compose Maes for complex workflows