Inside the Architecture: Patterns, Data Modeling, and Extensibility in Mes Recettes

Layered architecture view of Mes Recettes (Blazor + Supabase)

Inside the Architecture: Patterns, Data Modeling, and Extensibility in Mes Recettes

Part 2 of 3 in the Mes Recettes series: Part 1 – Overview · Part 3 – Testing & Delivery | 🇫🇷 Version

Layered View

While early iterations keep things lean, the design leans toward a clean-ish architecture:

B f S P l u u o a S t p s z e u D a t o r r o b g r v e m a r i a s S e P c R i e D S a e e n K Q g s c C L e i M l s ( p o i | A e d e & u S e n t v l t C h c s o , ) m p o n e n t s

Component Composition

App.razorMainLayout → page components (Recipes.razor, Books.razor, Authors.razor) with dialog components for CRUD.

Benefits:

  • Shared layout styling
  • Dialog-based create/edit flows (fast perceived performance)
  • Room to inject cross-cutting concerns later (loading, notifications)

Data Modeling Strategy

Direct table mapping via attributes:

Concept Table Relationship
Author authors M:N with Books
Book books 1:N Recipes, M:N Authors
Recipe recettes Optional Book FK
BookAuthor books_authors Junction

Mermaid ER (simplified):

F i A e i f l U l d i a T d r s H s s t O : t _ R _ n n a a m m e e M : N ( B l & O i O n b K k o A s o U k T a s H u ) O t R h o r s M : N F i i n r b e d a a o l m t o d e i k s n _ B R : g i O E d O C K I 1 P o : E p N t i o n a l )

Validation Approach

  • DataAnnotations enforce UI + server consistency.
  • Unit tests assert boundaries (e.g., rating range).
  • Future: FluentValidation layer if rules become contextual.

Patterns Documented (Future Hardening)

Pattern Why When to Activate
Repository Swap storage, mock easier When domain logic grows
Unit of Work Group transactions If multi-entity changes need ACID boundaries
Caching Decorator Reduce duplicate reads After identifying hot paths
Specification Complex querying When queries become conditional & repetitive

Example: Repository (Deferred)

public class SupabaseRepository<T> : IRepository<T> where T : BaseModel, new()
{
    private readonly SupabaseClient _client;
    public async Task<List<T>> GetAllAsync() =>
        (await _client.From<T>().Get()).Models ?? new();
}

Not activated yet to avoid premature abstraction.

Real-Time & Rehydration

Supabase Realtime is pre-enabled (AutoConnectRealtime = true). Once recipe channels are subscribed:

  1. Listen to INSERT/UPDATE/DELETE
  2. Apply optimistic UI updates
  3. Optionally reconcile after silent refetch

Extensibility Points

Extension Hook
Offline mode Service worker / local storage caching
Search PostgreSQL full-text search + index
Theming MudBlazor palette service
Audit logging Database triggers → Append-only audit table
Analytics Minimal event bus (in-memory → remote sink)

Trade-Offs

Decision Upside Downside
Direct model → table mapping Simplicity, speed Coupling to schema
Minimal service layer early Fast iteration Harder to inject cross-cutting concerns later
Supabase BaaS Auth + Realtime + DB packaged Vendor feature surface sets constraints
WASM-only (no server) CDN deploy, low hosting cost Larger initial payload, no server prerender

Migration Strategy (If Needed)

  1. Introduce Server (ASP.NET) for prerender + protected APIs.
  2. Carve out domain-specific service boundaries.
  3. Add message broker if collaboration logic grows (Redis / Azure Service Bus).
  4. Gradually move from direct Supabase client usage in UI to mediated services.

Checklist for Future You

  • Add RLS policies (enforce ownership)
  • Implement repository/caching only when metrics show need
  • Introduce feature flags (simple JSON-driven) before major experiments
  • Add DB schema drift tracking (e.g., pg_dump + diff in CI)
  • Track bundle size budget (target < 2 MB compressed)

Source: https://github.com/mongeon/RecettesIndex



See also