Ticket Masala: Architecture Review & Improvement Recommendations¶
Date: January 2025
Reviewer: Architecture Analysis
Purpose: Current state assessment and recommendations for architectural improvements
Executive Summary¶
Ticket Masala is a well-structured modular monolith with strong domain-agnostic foundations. The architecture demonstrates good separation of concerns, extensibility patterns, and multi-tenancy support. This review identifies areas for improvement to enhance maintainability, flexibility, and integration capabilities.
Key Findings: - Strong modular structure with clear boundaries - Good use of dependency injection and extension methods - Multi-tenant support via Docker volumes - Inconsistent naming conventions (Ticket vs WorkItem) - Tight coupling to SQLite-specific features - Missing standardized plugin interface alignment - Configuration management could be more flexible
1. Current Architecture Analysis¶
1.1 Architecture Pattern¶
Current: Modular Monolith with Plugin System
┌─────────────────────────────────────────┐
│ TicketMasala.Web │
│ ┌───────────────────────────────────┐ │
│ │ Controllers (MVC + API) │ │
│ └──────────────┬────────────────────┘ │
│ │ │
│ ┌──────────────▼────────────────────┐ │
│ │ Engine/ (Business Logic) │ │
│ │ ├── Core/ │ │
│ │ ├── GERDA/ (AI Services) │ │
│ │ ├── Compiler/ (Rule Engine) │ │
│ │ └── Ingestion/ │ │
│ └──────────────┬────────────────────┘ │
│ │ │
│ ┌──────────────▼────────────────────┐ │
│ │ Repositories/ (Data Access) │ │
│ └──────────────┬────────────────────┘ │
│ │ │
│ ┌──────────────▼────────────────────┐ │
│ │ Data/ (EF Core DbContext) │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
Strengths: - Clear separation between presentation, business logic, and data access - Well-organized Engine modules with single responsibilities - Repository pattern provides testability and abstraction
Weaknesses: - No clear domain boundaries (everything in one project) - Missing shared domain models project (domain models embedded in Web project) - Controllers mix MVC and API concerns
1.2 Dependency Injection & Service Registration¶
Current Pattern:
// Extension methods organize service registration
builder.Services.AddMasalaDatabase(...);
builder.Services.AddCoreServices();
builder.Services.AddGerdaServices(...);
Assessment: Excellent - Clean, discoverable extension methods - Follows ASP.NET Core best practices - Easy to test and mock
Recommendation: Consider consolidating into fewer extension methods for consistency, following established ASP.NET Core patterns.
1.3 Multi-Tenancy Architecture¶
Current Implementation:
- Tenant Isolation: Docker volumes per tenant (tenants/{tenant}/config, tenants/{tenant}/data)
- Configuration: Environment variable MASALA_CONFIG_PATH + TenantConnectionResolver
- Plugin System: TenantPluginLoader loads external DLLs
Strengths: - Clear tenant separation via volumes - Each tenant has isolated database - Plugin system allows tenant-specific extensions
Weaknesses: - No runtime tenant resolution (requires separate containers) - Plugin interface could be standardized for better interoperability - No shared tenant registry/metadata
Recommendation: Consider supporting both patterns: 1. Current (Container-per-tenant): Good for strict isolation 2. Runtime resolution: Better for shared infrastructure and resource efficiency
1.4 Data Model & Persistence¶
Current: - EF Core with SQLite (primary) or SQL Server - Repository + Unit of Work pattern - Specification pattern for queries
Strengths:
- Repository abstraction enables testing
- Specification pattern provides reusable queries
- JSON columns (CustomFieldsJson) for flexibility
Critical Issues:
Issue 1: SQLite-Specific Computed Columns¶
// MasalaDbContext.cs - Line 55-66
entity.Property(e => e.ComputedPriority)
.HasComputedColumnSql("json_extract(CustomFieldsJson, '$.priority_score')", stored: true);
Problem: This SQLite-specific syntax won't work with SQL Server or PostgreSQL.
Recommendation: Use EF Core's database-agnostic approach:
// Option 1: Database-agnostic computed column
entity.Property(e => e.ComputedPriority)
.HasComputedColumnSql(
"CASE " +
"WHEN 'SQLite' THEN json_extract(CustomFieldsJson, '$.priority_score') " +
"WHEN 'SqlServer' THEN JSON_VALUE(CustomFieldsJson, '$.priority_score') " +
"ELSE NULL END",
stored: true);
// Option 2: Use EF Core's HasComputedColumnSql with provider-specific SQL
// Better: Use a database provider factory pattern
Issue 2: Missing Domain Abstraction¶
The Ticket model mixes domain concepts:
- Core fields (Title, Description, Status)
- Domain-specific fields (DomainId, WorkItemTypeCode)
- JSON blobs (CustomFieldsJson, DomainCustomFieldsJson)
Recommendation: Extract to shared domain project:
src/
├── TicketMasala.Domain/ # NEW: Shared domain models
│ ├── Entities/
│ │ ├── WorkItem.cs # Rename from Ticket
│ │ ├── WorkContainer.cs # Rename from Project
│ │ └── WorkHandler.cs # Rename from ApplicationUser
│ └── ValueObjects/
└── TicketMasala.Web/ # Presentation & Infrastructure
1.5 Configuration Management¶
Current:
- YAML-based domain configuration (masala_domains.yaml)
- JSON feature flags (masala_config.json)
- Environment variable resolution (MASALA_CONFIG_PATH)
Strengths: - Flexible YAML-based domain rules - Clear configuration path resolution
Weaknesses: - No configuration validation on startup - No hot-reload support (requires app restart) - Configuration scattered across multiple files
Recommendation: Use strongly-typed configuration options:
// Strongly-typed options pattern
public class MasalaOptions
{
public string ConfigPath { get; set; }
public DatabaseOptions Database { get; set; }
public GerdaOptions Gerda { get; set; }
}
// In Program.cs
builder.Services.Configure<MasalaOptions>(builder.Configuration.GetSection("Masala"));
1.6 GERDA AI Architecture¶
Current Structure:
Engine/GERDA/
├── Dispatching/ # Agent assignment
├── Estimating/ # Effort estimation
├── Ranking/ # Priority scoring
├── Grouping/ # Spam detection
└── Anticipation/ # Forecasting
Strengths:
- Clear separation of AI concerns
- Strategy pattern for swappable algorithms
- Background processing via GerdaBackgroundService
Weaknesses: - ML.NET models embedded in application - No model versioning or A/B testing - No metrics/observability for AI decisions
Recommendation: Consider extracting AI services to a separate project for better separation of concerns:
src/
├── TicketMasala.Web/ # Main application (Operations)
└── TicketMasala.AI/ # AI/ML services (Optional)
└── Gerda/
2. Integration Challenges & Solutions¶
2.1 Naming Convention Mismatch¶
Problem: Ticket Masala uses "Ticket" internally but documents "WorkItem" in API.
Current State:
- Internal code: Ticket, Project, ApplicationUser
- API documentation: WorkItem, WorkContainer, WorkHandler
- Configuration: Mixed terminology
Impact: Confusing for developers and API consumers due to inconsistent terminology.
Recommendation: 1. Phase 1: Add aliases in API layer
[Route("api/v1/[controller]")]
[ApiController]
public class WorkItemsController : ControllerBase
{
private readonly ITicketService _ticketService;
[HttpGet]
public async Task<IActionResult> GetWorkItems()
{
var tickets = await _ticketService.GetAllTicketsAsync();
return Ok(tickets.Select(t => MapToWorkItemDto(t)));
}
}
- Phase 2: Gradually migrate internal code to domain-agnostic terms
- Create
WorkItemas alias forTicket(via inheritance or composition) - Update new code to use
WorkItem - Deprecate
Ticketgradually
2.2 Plugin Interface Alignment¶
Problem: Plugin interface could be standardized for better interoperability with other plugin-based systems.
Current:
public interface ITenantPlugin
{
string TenantId { get; }
string DisplayName { get; }
void ConfigureServices(IServiceCollection services, IConfiguration configuration);
void ConfigureMiddleware(IApplicationBuilder app, IWebHostEnvironment env);
}
Recommendation: Consider creating a standardized plugin interface that can be adapted to various plugin ecosystems:
public interface IStandardPlugin
{
string PluginId { get; }
string DisplayName { get; }
void ConfigureServices(IServiceCollection services, IConfiguration configuration);
}
Create adapter pattern to bridge between interfaces as needed.
2.3 Database Provider Abstraction¶
Problem: SQLite-specific features prevent easy migration to SQL Server/PostgreSQL.
Current Issues: 1. Computed columns use SQLite syntax 2. JSON extraction functions are SQLite-specific 3. WAL mode configuration hardcoded
Recommendation: Use EF Core's database provider abstraction:
public static class DatabaseExtensions
{
public static void ConfigureComputedColumns(this ModelBuilder modelBuilder, string provider)
{
var ticketEntity = modelBuilder.Entity<Ticket>();
if (provider == "SQLite")
{
ticketEntity.Property(e => e.ComputedPriority)
.HasComputedColumnSql("json_extract(CustomFieldsJson, '$.priority_score')", stored: true);
}
else if (provider == "SqlServer")
{
ticketEntity.Property(e => e.ComputedPriority)
.HasComputedColumnSql("JSON_VALUE(CustomFieldsJson, '$.priority_score')", stored: true);
}
else if (provider == "PostgreSQL")
{
ticketEntity.Property(e => e.ComputedPriority)
.HasComputedColumnSql("(CustomFieldsJson->>'priority_score')::float", stored: true);
}
}
}
2.4 Shared Domain Models¶
Problem: No shared domain project for cross-module integration.
Current: All models in TicketMasala.Web/Models/
Recommendation: Extract to TicketMasala.Domain/:
src/
├── TicketMasala.Domain/ # NEW
│ ├── Entities/
│ │ ├── WorkItem.cs
│ │ ├── WorkContainer.cs
│ │ └── WorkHandler.cs
│ ├── ValueObjects/
│ │ ├── Status.cs
│ │ └── Priority.cs
│ ├── Interfaces/
│ │ ├── IWorkItemRepository.cs
│ │ └── IWorkItemService.cs
│ └── MasalaDbContext.cs # Move from Web project
│
├── TicketMasala.Web/ # Presentation only
│ ├── Controllers/
│ ├── Views/
│ └── ViewModels/ # DTOs for presentation
│
└── TicketMasala.Infrastructure/ # NEW: Data access
├── Repositories/
└── Data/
Benefits: - Domain models reusable across modules - Clear separation of concerns - Easier integration with other systems and modules
3. Specific Improvement Recommendations¶
3.1 High Priority (Integration Blockers)¶
3.1.1 Extract Domain Models¶
Action: Create TicketMasala.Domain project
- Move Models/ to Domain/Entities/
- Move MasalaDbContext to Domain/
- Update namespaces
Impact: Enables shared domain models across modules and systems
3.1.2 Standardize Plugin Interface¶
Action: Create standardized plugin interface - Create compatibility layer/adapter if needed - Update plugin loader
Impact: Enables plugin interoperability with other plugin-based systems
3.1.3 Database Provider Abstraction¶
Action: Remove SQLite-specific code - Abstract computed column SQL - Use EF Core provider detection - Test with SQL Server
Impact: Enables deployment flexibility
3.2 Medium Priority (Architecture Improvements)¶
3.2.1 Configuration Validation¶
Action: Add startup validation
public class MasalaConfigurationValidator
{
public void Validate(MasalaOptions options)
{
if (!Directory.Exists(options.ConfigPath))
throw new InvalidOperationException($"Config path not found: {options.ConfigPath}");
// Validate YAML syntax
// Validate required domains exist
}
}
3.2.2 Hot Reload Support¶
Action: Add configuration change detection
public class ConfigurationWatcher : IHostedService
{
private readonly FileSystemWatcher _watcher;
private readonly IDomainConfigurationService _configService;
public async Task StartAsync(CancellationToken cancellationToken)
{
_watcher.Changed += async (sender, e) =>
{
await _configService.ReloadConfigurationAsync();
};
}
}
3.2.3 API Versioning¶
Action: Add API versioning support
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
});
3.3 Low Priority (Nice to Have)¶
3.3.1 Observability¶
- Add OpenTelemetry instrumentation
- Add structured logging with Serilog
- Add metrics endpoints
3.3.2 Testing Infrastructure¶
- Add integration test base classes
- Add test data builders
- Add API contract testing
3.3.3 Documentation¶
- Generate API documentation (Swagger/OpenAPI)
- Add architecture decision records (ADRs)
- Add developer onboarding guide
4. Integration Roadmap¶
Phase 1: Foundation (Weeks 1-2)¶
- Extract
TicketMasala.Domainproject - Standardize plugin interface (
IStandardPlugin) - Abstract database provider code
- Add configuration validation
Phase 2: Alignment (Weeks 3-4)¶
- Align naming conventions (WorkItem terminology)
- Add API versioning
- Create shared domain abstractions
- Update documentation
Phase 3: Integration & Testing (Weeks 5-6)¶
- Create integration examples and documentation
- Test multi-tenant scenarios
- Performance testing
- Security audit
Phase 4: Enhancement (Ongoing)¶
- Add observability
- Improve test coverage
- Optimize performance
- Add advanced features
5. Code Quality Assessment¶
5.1 Strengths¶
- Clean Architecture: Clear separation of concerns
- Dependency Injection: Well-structured service registration
- Repository Pattern: Good data access abstraction
- Extension Methods: Discoverable configuration
- Observer Pattern: Decoupled event handling
5.2 Areas for Improvement¶
- Test Coverage: Limited integration tests
- Error Handling: Inconsistent exception handling
- Logging: Basic logging, needs structured logging
- Documentation: API documentation incomplete
- Performance: No caching strategy documented
5.3 Technical Debt¶
- SQLite Dependency: Computed columns need abstraction
- Naming Inconsistency: Ticket vs WorkItem confusion
- Configuration: No validation or hot-reload
- Plugin System: Could benefit from standardization
- Domain Models: Embedded in Web project
6. Recommendations Summary¶
Critical (Must Fix)¶
- Extract domain models to separate project
- Abstract database provider code
- Standardize plugin interface
Important (Should Fix)¶
- Add configuration validation
- Align naming conventions
- Add API versioning
Nice to Have¶
- Add observability
- Improve test coverage
- Add hot-reload support
7. Conclusion¶
Ticket Masala has a solid architectural foundation with good separation of concerns and extensibility patterns. The main integration challenges are:
- Naming inconsistencies (Ticket vs WorkItem)
- Database provider coupling (SQLite-specific code)
- Plugin interface standardization (for better interoperability)
- Missing domain abstraction (models embedded in Web project)
With the recommended improvements, Ticket Masala will be more maintainable, flexible, and ready for integration with various systems while maintaining its domain-agnostic, multi-tenant architecture.
Next Steps: 1. Review this document with the team 2. Prioritize improvements based on integration timeline 3. Create GitHub issues for each recommendation 4. Begin Phase 1 implementation
Document Version: 1.0
Last Updated: January 2025