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:

BfSPluuoaStpszeuDatorrobgrvemariasSePcRieDSaeenKQgscCLeiMls(poi|Aede&uSentvltChcso,)mponents

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:

ConceptTableRelationship
AuthorauthorsM:N with Books
Bookbooks1:N Recipes, M:N Authors
ReciperecettesOptional Book FK
BookAuthorbooks_authorsJunction

Mermaid ER (simplified):

FiAeiflUldiaTdrsHsstO:t_R_nnaammeeM:N(Bl&OiOnbKkoAsoUkTasHu)OtRhorsM:NFiinrbedaaolmtodeiksn_BR:giOEdOCKI1Po:EpNtional)

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)

PatternWhyWhen to Activate
RepositorySwap storage, mock easierWhen domain logic grows
Unit of WorkGroup transactionsIf multi-entity changes need ACID boundaries
Caching DecoratorReduce duplicate readsAfter identifying hot paths
SpecificationComplex queryingWhen 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

ExtensionHook
Offline modeService worker / local storage caching
SearchPostgreSQL full-text search + index
ThemingMudBlazor palette service
Audit loggingDatabase triggers → Append-only audit table
AnalyticsMinimal event bus (in-memory → remote sink)

Trade-Offs

DecisionUpsideDownside
Direct model → table mappingSimplicity, speedCoupling to schema
Minimal service layer earlyFast iterationHarder to inject cross-cutting concerns later
Supabase BaaSAuth + Realtime + DB packagedVendor feature surface sets constraints
WASM-only (no server)CDN deploy, low hosting costLarger 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