-
-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture.md
This document describes the high-level architecture, design philosophy, and structural decisions of OpenCashFlow.
OpenCashFlow follows Clean Architecture principles with these goals:
- Separation of Concerns - Each layer has a single responsibility
- Dependency Inversion - High-level modules don't depend on low-level modules
- Testability - Business logic is isolated from infrastructure
- Maintainability - Changes in one layer don't cascade to others
┌─────────────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ OpenCashFlow │ │ OpenCashFlow │ │ OpenCashFlow │ │
│ │ .App │ │ .API │ │ .Admin │ │
│ │ (MVC + UI) │ │ (REST API) │ │ (Admin Panel) │ │
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
└───────────┼────────────────────┼────────────────────┼───────────┘
│ │ │
└────────────────────┼────────────────────┘
│
┌────────────────────────────────┼────────────────────────────────┐
│ Shared Layer │
│ ┌─────────────────────────────┴─────────────────────────────┐ │
│ │ OpenCashFlow.Shared │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ Models │ │ DTOs │ │ Services │ │ Repositories │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Enums │ │ Mappings │ │ DbContext│ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────┬───────────────────────────────┘
│
┌─────────────────────────────────┼───────────────────────────────┐
│ Data Layer │
│ ┌──────────────────────────────┴────────────────────────────┐ │
│ │ PostgreSQL 16 │ │
│ └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
The presentation layer contains three separate applications:
| Project | Responsibility |
|---|---|
| OpenCashFlow.App | User-facing MVC application with Razor views, handles UI rendering and user authentication flows |
| OpenCashFlow.API | RESTful API for data operations, consumed by the App via AJAX and by external integrations |
| OpenCashFlow.Admin | Administrative dashboard for system management and monitoring |
The shared library (OpenCashFlow.Shared) contains:
- Models (Entities) - Database entity definitions with EF Core mappings
- DTOs - Data Transfer Objects for API contracts
- Services - Business logic implementations
- Repositories - Data access abstractions
- Enums - Shared enumerations (permissions, statuses, types)
- Mappings - AutoMapper profiles for entity-DTO conversion
- DbContext - Entity Framework Core database context
PostgreSQL database with:
- Multi-tenant data isolation via
TenantID - Audit trail fields on all entities
- Soft delete support
- Optimistic concurrency where needed
Every business entity includes a TenantID field that represents the owning company:
public class Payment
{
public Guid PaymentID { get; set; }
public Guid TenantID { get; set; } // Company identifier
public decimal Amount { get; set; }
// ... other fields
}All queries filter by TenantID to ensure data isolation between companies.
Data access is abstracted through repository interfaces:
// Interface definition
public interface IPaymentRepository
{
Task<IEnumerable<Payment>> GetAllAsync(Guid tenantId);
Task<Payment?> GetByIdAsync(Guid paymentId);
Task<Payment> CreateAsync(Payment payment);
Task UpdateAsync(Payment payment);
Task DeleteAsync(Guid paymentId);
}
// Controller usage
public class PaymentController : ControllerBase
{
private readonly IPaymentRepository _paymentRepository;
public PaymentController(IPaymentRepository paymentRepository)
{
_paymentRepository = paymentRepository;
}
}DTOs separate API contracts from internal entities:
// Entity (internal)
public class Payment
{
public Guid PaymentID { get; set; }
public Guid TenantID { get; set; }
public decimal Amount { get; set; }
public string CreatedBy { get; set; }
public DateTime DateIns { get; set; }
// ... audit fields
}
// DTO (external contract)
public class Payment_List_DTO
{
public Guid PaymentID { get; set; }
public decimal Amount { get; set; }
public string EntryType { get; set; }
public DateTime PaymentDate { get; set; }
}Controllers and repositories are split using partial classes for maintainability:
Controllers/
PaymentController.cs # Main controller definition
PaymentController.Create.cs # Create payment logic
PaymentController.Update.cs # Update payment logic
PaymentController.Delete.cs # Delete payment logic
Startup configuration is organized into chainable extension methods:
// Program.cs
builder.AppStartConfigureLogging();
builder.AppStartConfigureServices();
builder.AppStartConfigureAuthentication();
// Extension method files
// AppStart/00_Logging.cs
// AppStart/01_Configuration.cs
// AppStart/02_Authentication.csHTTP Request
│
▼
┌─────────────────────┐
│ Middleware │
│ - Authentication │
│ - Authorization │
│ - Subscription │
│ - Error Handling │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Controller │
│ - Route handling │
│ - Input validation │
│ - Response mapping │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Service │
│ - Business logic │
│ - Validation rules │
│ - Orchestration │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Repository │
│ - Data access │
│ - Query building │
│ - CRUD operations │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ DbContext │
│ - EF Core │
│ - Change tracking │
│ - Migrations │
└──────────┬──────────┘
│
▼
PostgreSQL
HTTP Request
│
▼
┌─────────────────────┐
│ MVC Controller │
│ - Authentication │
│ - View selection │
└──────────┬──────────┘
│
├─────────────────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Razor View │ │ AJAX Call │
│ (server) │ │ to API │
└─────────────┘ └──────┬──────┘
│
▼
┌─────────────┐
│ OpenCash │
│ Flow.API │
└─────────────┘
All entities inherit audit fields for compliance and debugging:
| Field | Purpose |
|---|---|
CreatedBy |
User who created the record |
DateIns |
Creation timestamp |
EditedBy |
User who last modified the record |
DateEdit |
Last modification timestamp |
IsDeleted |
Soft delete flag |
IsDeletedBy |
User who deleted the record |
IsDeletedWhy |
Reason for deletion |
DateDeleted |
Deletion timestamp |
All API responses use a consistent wrapper:
public class ApiResponse<T>
{
public bool Success { get; set; }
public string Message { get; set; }
public T Data { get; set; }
public List<string> Errors { get; set; }
}Example responses:
// Success
{
"success": true,
"message": "Payment created successfully",
"data": { "paymentId": "..." },
"errors": []
}
// Error
{
"success": false,
"message": "Validation failed",
"data": null,
"errors": ["Amount must be greater than zero"]
}Services are registered in Program.cs with appropriate lifetimes:
// Scoped (per-request)
builder.Services.AddScoped<IPaymentRepository, PaymentRepository>();
builder.Services.AddScoped<IPaymentService, PaymentService>();
// Singleton (application lifetime)
builder.Services.AddSingleton<IEmailService, EmailService>();
// Transient (new instance each time)
builder.Services.AddTransient<IValidator<PaymentDto>, PaymentValidator>();| Service | Purpose |
|---|---|
| Stripe | Subscription billing, payment processing, webhooks |
| Sentry | Error tracking and monitoring |
| Slack | Log notifications (via Serilog) |
| SMTP | Email delivery |
Project status
OpenCashFlow is under active development.
APIs, database schema, and UI may change until the first stable release.
Built with
.NET · ASP.NET Core · Entity Framework Core · PostgreSQL · Tabler
© 2026 OpenCashFlow
- Developer Preview
- Not production-ready
- First-run setup included