Expert guidance for developing Agenti, a banking agency ERP system built with ASP.NET Core Blazor Server (.NET 10), PostgreSQL, Entity Framework Core, and MudBlazor. Covers vertical slice architecture, vault management, cash sessions, and compliance workflows.
Expert guidance for developing **Agenti**, a banking agency ERP system managing cash operations, transactions, vault management, and discrepancy workflows for banking agencies in Uganda.
The system uses **Vertical Slice Architecture** where each feature is self-contained:
```
Features/{FeatureName}/
├── {Feature}Dtos.cs # Request/response DTOs
├── I{Feature}Service.cs # Service interface
└── {Feature}Service.cs # Business logic + data access
```
**Services combine business logic and data access** - no separate repository layer. All services inject `ApplicationDbContext` directly.
**Start PostgreSQL:**
```bash
docker-compose up -d
docker-compose exec postgres psql -U agenti_user -d agenti_dev -c "SELECT 1"
```
**Apply Migrations:**
```bash
cd EastSeat.Agenti.Web
dotnet ef database update
```
**Create Migration:**
```bash
dotnet ef migrations add MigrationName --output-dir Data/Migrations
```
**Connection String Format:**
```
Server=localhost;Port=5432;Database=agenti_dev;User Id=<username>;Password=<password>;
```
```bash
dotnet build
cd EastSeat.Agenti.Web
dotnet run
```
**Application URLs:**
**Step-by-step process:**
1. **Create feature folder:**
```
Features/{FeatureName}/
```
2. **Create DTOs file:**
```csharp
// {Feature}Dtos.cs
namespace EastSeat.Agenti.Web.Features.{FeatureName};
public record {Feature}Request(...);
public record {Feature}Response(...);
```
3. **Create service interface:**
```csharp
// I{Feature}Service.cs
public interface I{Feature}Service
{
Task<Result> OperationAsync(...);
}
```
4. **Implement service:**
```csharp
// {Feature}Service.cs
public class {Feature}Service : I{Feature}Service
{
private readonly ApplicationDbContext _context;
public {Feature}Service(ApplicationDbContext context)
{
_context = context;
}
// Implementation
}
```
5. **Register in Program.cs:**
```csharp
builder.Services.AddScoped<I{Feature}Service, {Feature}Service>();
```
6. **Create Blazor component:**
```razor
@page "/{feature-route}"
@inject I{Feature}Service Service
@attribute [Authorize(Policy = "PolicyName")]
<MudContainer>
<MudCard>
<MudCardHeader>
<MudText Typo="Typo.h5">Feature Title</MudText>
</MudCardHeader>
<MudCardContent>
@* Content *@
</MudCardContent>
</MudCard>
</MudContainer>
@code {
// Component logic
}
```
7. **Add navigation (if needed):**
Update `Components/Layout/NavMenu.razor`
1. **Create entity class:**
```csharp
// Shared/Domain/Entities/{EntityName}.cs
public class {EntityName}
{
public int Id { get; set; }
// Properties
}
```
2. **Add to DbContext:**
```csharp
// ApplicationDbContext.cs
public DbSet<{EntityName}> {EntityNames} { get; set; }
```
3. **Configure in OnModelCreating:**
```csharp
modelBuilder.Entity<{EntityName}>(entity =>
{
entity.HasKey(e => e.Id);
// Relationships, indexes, precision
});
```
4. **Create and apply migration:**
```bash
dotnet ef migrations add Add{EntityName}
dotnet ef database update
```
**Define in Program.cs:**
```csharp
builder.Services.AddAuthorizationBuilder()
.AddPolicy("PolicyName", policy =>
policy.RequireRole(UserRole.Admin.ToString()));
```
**Apply to components:**
```razor
@attribute [Authorize(Policy = "PolicyName")]
```
**Conditional UI:**
```razor
<AuthorizeView Policy="PolicyName">
<Authorized>
@* Content for authorized users *@
</Authorized>
<NotAuthorized>
@* Alternative content *@
</NotAuthorized>
</AuthorizeView>
```
**Branch & Vault (1:1):**
**Users & Agents:**
**Cash Sessions:**
**Vault Transactions:**
**Discrepancies:**
```
Branch 1:1 Vault
ApplicationUser 1:1 Agent
Agent 1:N Wallet (unique: AgentId + WalletTypeId)
CashSession 1:N CashCount, Transaction, Discrepancy
Vault 1:N VaultTransaction
```
**Concurrency Safety:**
**Workflow:**
1. **Opening Session:**
- `VaultService.WithdrawForSessionAsync()`
- Deducts from vault
- Populates agent wallets
2. **Closing Session:**
- `VaultService.DepositForSessionAsync()`
- Returns to vault
- Zeros out wallets
3. **Manual Adjustments:**
- Creates `VaultTransaction` with `Status=Pending`, `ExpiresAt=12h`
- Requires Admin approval via `ApproveManualAdjustmentAsync()`
- **Dual approval enforced:** creator ≠ approver
**Fraud Prevention:**
**Opening Count Rules:**
**Closing Count Rules:**
**Discrepancy Workflow:**
On first run, app redirects to `/setup-prerequisites`:
1. Creates default branch
2. Creates admin user
3. Sets `AppConfig["SetupComplete"] = "true"`
4. Normal operation begins
Current vertical slices:
```bash
docker ps | grep agenti-postgres
docker-compose logs -f postgres
docker-compose exec postgres psql -U agenti_user -d agenti_dev -c "SELECT 1"
```
Enable EF Core query logging in `appsettings.json`:
```json
{
"Logging": {
"LogLevel": {
"Microsoft.EntityFrameworkCore": "Information"
}
}
}
```
When working with this codebase:
1. **Always follow vertical slice architecture** - keep features self-contained
2. **Use PostgreSQL-specific features** when needed (row-level locks, serializable isolation)
3. **Apply authorization policies** to all sensitive operations
4. **Maintain immutable audit trails** for financial operations
5. **Enforce dual-approval** for vault adjustments
6. **Use MudBlazor components** consistently across UI
7. **Register services in Program.cs** as Scoped
8. **Apply migrations** after entity changes
9. **Follow decimal precision** rules for money fields
10. **Use DateTimeOffset (UTC)** for all timestamps
11. **Test concurrency scenarios** for vault operations
12. **Validate business rules** (opening = previous closing, dual approval, etc.)
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/agent-banking-erp-guidance/raw