Przejdź do treści

Architektura systemu Masaku

Wprowadzenie

System Masaku składa się z dwóch głównych komponentów: - masaku-portal - Frontend (Angular 14 + Nx) - masaku-api - Backend (.NET C# + Entity Framework)

Architektura high-level

┌─────────────────────────────────────────────────────────────┐
│                    Użytkownicy                              │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              Azure AD B2C (Autentykacja)                    │
└─────────────────────────────────────────────────────────────┘
                ┌─────────────┴─────────────┐
                ▼                           ▼
┌───────────────────────────┐   ┌───────────────────────────┐
│   masaku-portal (SPA)     │   │   masaku-api (.NET)       │
│   - Angular 14            │   │   - ASP.NET Core          │
│   - Nx Monorepo           │   │   - RESTful API           │
│   - NGXS State            │◄──┤   - Entity Framework      │
│   - Azure Blob Storage    │   │   - SQL Server            │
└───────────────────────────┘   └───────────────────────────┘
                    ┌─────────────────────────┼─────────────┐
                    ▼                         ▼             ▼
          ┌─────────────────┐   ┌──────────────────┐  ┌────────────┐
          │   SQL Server    │   │  Azure Storage   │  │  MailHog   │
          │   (Database)    │   │  (Files/Blobs)   │  │  (Email)   │
          └─────────────────┘   └──────────────────┘  └────────────┘

Architektura frontendowa (masaku-portal)

Struktura Nx Monorepo

Portal wykorzystuje Nx workspace z 32+ bibliotekami zorganizowanymi według domeny:

masaku-portal/
├── apps/
│   └── masaku/              # Główna aplikacja Angular
└── projects/                # Biblioteki (32+)
    ├── common/              # Współdzielone utilities
    ├── types/               # TypeScript interfaces
    ├── budgets/             # Moduł budżetów
    ├── clients/             # Moduł klientów
    ├── expenses/            # Moduł wydatków
    ├── invoices/            # Moduł faktur
    ├── receipts/            # Moduł paragonów
    ├── store/               # NGXS state management
    └── ...                  # Pozostałe moduły

Wzorce architektoniczne

1. Feature-based organization

Każda funkcjonalność biznesowa (budżety, klienci, wydatki) jest osobną biblioteką Nx:

// Import z innej biblioteki
import { BudgetService } from '@msk/budgets';
import { ClientService } from '@msk/clients';

2. Layered architecture w bibliotekach

Każda biblioteka ma warstwową strukturę:

projects/budgets/
├── components/          # Komponenty UI
├── services/            # Logika biznesowa
├── state/              # NGXS state
├── models/             # TypeScript interfaces
├── constants/          # Stałe
└── utils/              # Funkcje pomocnicze

3. State management (NGXS)

Centralizacja stanu aplikacji z NGXS:

// State class
@State<BudgetStateModel>({
  name: 'budgets',
  defaults: { list: [], selected: null }
})
export class BudgetState {
  @Action(LoadBudgets)
  loadBudgets(ctx: StateContext<BudgetStateModel>) {
    // ...
  }
}

// Komponent
export class BudgetListComponent {
  budgets$ = this.store.select(BudgetState.getBudgets);

  constructor(private store: Store) {}
}

4. Abstract base classes

Reużywalne klasy bazowe dla typowych wzorców:

// projects/common/src/lib/services/abstract-api.service.ts
export abstract class AbstractApiService<T> {
  abstract getAll(): Observable<T[]>;
  abstract getById(id: string): Observable<T>;
  abstract create(item: T): Observable<T>;
  abstract update(id: string, item: T): Observable<T>;
  abstract delete(id: string): Observable<void>;
}

// Implementacja
export class BudgetService extends AbstractApiService<Budget> {
  // Implementacja specificzna dla budżetów
}

5. Reactive programming (RxJS)

Wszystkie operacje async używają RxJS:

// Przykład: łączenie wielu strumieni
combineLatest([
  this.budgetService.getAll(),
  this.clientService.getAll(),
  this.userService.getCurrentUser()
]).pipe(
  map(([budgets, clients, user]) => ({
    budgets: budgets.filter(b => b.userId === user.id),
    clients
  }))
).subscribe(result => {
  // Przetwarzanie
});

Architektura backendowa (masaku-api)

Clean Architecture / Layered Architecture

API wykorzystuje warstwową architekturę:

masaku-api/
├── Masaku.API/              # Warstwa prezentacji
│   ├── Controllers/         # REST endpoints
│   ├── Middlewares/         # Request pipeline
│   └── Extensions/          # DI configuration
├── Masaku.Services/         # Warstwa logiki biznesowej
│   ├── Services/            # Business logic
│   ├── Validators/          # Validation
│   └── Mappers/             # DTO mapping
├── Masaku.Repository/       # Warstwa dostępu do danych
│   ├── Repositories/        # Data access
│   ├── Context/             # EF DbContext
│   └── Migrations/          # Liquibase
└── Masaku.Domain/           # Warstwa domenowa
    ├── Entities/            # Domain models
    └── Interfaces/          # Abstrakcje

Zależności między warstwami

┌────────────────────────────────────────┐
│         Masaku.API                     │
│   (Controllers, Middleware)            │
└────────────────┬───────────────────────┘
                 │ depends on
┌────────────────────────────────────────┐
│      Masaku.Services                   │
│   (Business Logic)                     │
└────────────────┬───────────────────────┘
                 │ depends on
┌────────────────────────────────────────┐
│     Masaku.Repository                  │
│   (Data Access)                        │
└────────────────┬───────────────────────┘
                 │ depends on
┌────────────────────────────────────────┐
│       Masaku.Domain                    │
│   (Entities, Interfaces)               │
└────────────────────────────────────────┘

Dependency Injection

ASP.NET Core DI container zarządza wszystkimi zależnościami:

// Masaku.API/Extensions/ServiceCollectionExtensions.cs
public static IServiceCollection AddApplicationServices(
    this IServiceCollection services)
{
    // Repositories
    services.AddScoped<IBudgetRepository, BudgetRepository>();

    // Services
    services.AddScoped<IBudgetService, BudgetService>();

    // Validators
    services.AddScoped<IValidator<Budget>, BudgetValidator>();

    return services;
}

Repository Pattern

Abstrakcja dostępu do danych:

// Masaku.Domain/Interfaces/IBudgetRepository.cs
public interface IBudgetRepository
{
    Task<IEnumerable<Budget>> GetAllAsync();
    Task<Budget> GetByIdAsync(int id);
    Task<Budget> CreateAsync(Budget budget);
    Task UpdateAsync(Budget budget);
    Task DeleteAsync(int id);
}

// Masaku.Repository/Repositories/BudgetRepository.cs
public class BudgetRepository : IBudgetRepository
{
    private readonly MasakuDbContext _context;

    public BudgetRepository(MasakuDbContext context)
    {
        _context = context;
    }

    public async Task<IEnumerable<Budget>> GetAllAsync()
    {
        return await _context.Budgets.ToListAsync();
    }
    // ...
}

Komunikacja Frontend-Backend

REST API

Frontend komunikuje się z backendem przez RESTful API:

// Frontend: projects/budgets/src/lib/services/budget-api.service.ts
@Injectable()
export class BudgetApiService extends AbstractApiService<Budget> {
  private apiUrl = '/api/budgets';

  getAll(): Observable<Budget[]> {
    return this.http.get<Budget[]>(this.apiUrl);
  }

  getById(id: string): Observable<Budget> {
    return this.http.get<Budget>(`${this.apiUrl}/${id}`);
  }
}
// Backend: Masaku.API/Controllers/BudgetsController.cs
[ApiController]
[Route("api/[controller]")]
public class BudgetsController : ControllerBase
{
    private readonly IBudgetService _budgetService;

    [HttpGet]
    public async Task<IActionResult> GetAll()
    {
        var budgets = await _budgetService.GetAllAsync();
        return Ok(budgets);
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetById(int id)
    {
        var budget = await _budgetService.GetByIdAsync(id);
        return Ok(budget);
    }
}

Autentykacja i autoryzacja

Azure AD B2C

System używa Azure AD B2C do autentykacji:

// Frontend: MSAL configuration
export const msalConfig = {
  auth: {
    clientId: environment.azureClientId,
    authority: environment.azureAuthority,
    redirectUri: environment.redirectUri
  }
};
// Backend: JWT validation
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = configuration["AzureAdB2C:Authority"];
        options.Audience = configuration["AzureAdB2C:ClientId"];
    });

Authorization Flow

1. User → Frontend: Login request
2. Frontend → Azure AD B2C: Redirect to login
3. Azure AD B2C → Frontend: Return JWT token
4. Frontend → Backend: API request with Bearer token
5. Backend: Validate JWT, process request
6. Backend → Frontend: Response

Multi-region support

System obsługuje dwa regiony (DE i AT) z osobnymi:

Frontend

// src/environments/de/environment.prod.ts
export const environment = {
  production: true,
  apiUrl: 'https://api.masaku.de',
  region: 'DE'
};

// src/environments/at/environment.prod.ts
export const environment = {
  production: true,
  apiUrl: 'https://api.masaku.at',
  region: 'AT'
};

Backend

// appsettings.Germany.json
{
  "Region": "DE",
  "ConnectionStrings": {
    "DefaultConnection": "..."
  }
}

// appsettings.Austria.json
{
  "Region": "AT",
  "ConnectionStrings": {
    "DefaultConnection": "..."
  }
}

Bezpieczeństwo

Frontend

  1. JWT token storage: HttpOnly cookies (planned) lub sessionStorage
  2. XSS protection: Angular built-in sanitization
  3. CSRF protection: CSRF tokens w formularzach
  4. CSP headers: Content Security Policy

Backend

  1. Authentication: Azure AD B2C JWT validation
  2. Authorization: Role-based access control
  3. Input validation: FluentValidation
  4. SQL injection protection: Entity Framework parameterized queries
  5. CORS: Skonfigurowane allowed origins

Skalowanie i performance

Frontend

  • Lazy loading: Moduły ładowane on-demand
  • Bundle splitting: Separate chunks per route
  • AOT compilation: Ahead-of-time compilation
  • Tree shaking: Usuwanie nieużywanego kodu
  • CDN: Static assets z CDN

Backend

  • Caching: Redis (planned) lub in-memory
  • Connection pooling: EF Core connection pool
  • Async/await: Wszystkie I/O operations async
  • Load balancing: Azure App Service (multiple instances)

Monitoring i logging

Frontend

  • Error tracking: Sentry (planned) lub Application Insights
  • Analytics: Google Analytics / Azure Application Insights
  • Console logging: Environment-based log levels

Backend

  • Application Insights: Telemetria i metryki
  • Structured logging: Serilog
  • Health checks: ASP.NET Core health check endpoints
  • Exception handling: Global exception middleware

Diagram przepływu danych

User Action
┌─────────────────┐
│   Component     │
└────────┬────────┘
         │ dispatch action
┌─────────────────┐
│  NGXS Action    │
└────────┬────────┘
         │ calls
┌─────────────────┐
│  API Service    │
└────────┬────────┘
         │ HTTP request
┌─────────────────┐
│   Controller    │ (Backend)
└────────┬────────┘
         │ calls
┌─────────────────┐
│  Business Svc   │
└────────┬────────┘
         │ calls
┌─────────────────┐
│   Repository    │
└────────┬────────┘
         │ query
┌─────────────────┐
│    Database     │
└─────────────────┘

Dalsze kroki