Skip to main content

Domain & CRUD Principles

The core philosophy of DartWay:
➡️ everything starts from domain models
➡️ all Flutter ↔ Server interactions go through CRUD operations

This strict approach ensures consistency, predictability, and AI-friendly development.

info

DartWay follows the core principles of REST:

  • Models behave like REST resources
  • Operations map to standard HTTP verbs (POST, GET, PUT/PATCH, DELETE)

See REST architectural style for more details.


Domain-first mindset

  1. Think in models

    • Every feature begins with identifying entities.
    • Example: UserProfile, Product, Order, ChatMessage.
  2. Relations matter

    • Define 1–1, 1–many, many–many relations explicitly.
    • Models should reflect the domain reality, not just current UI needs.
  3. Single source of truth

    • Data lives in models.
    • UI is only a projection, never a source of logic.
    • No duplicated state outside the generic data layer.

CRUD-only principle

All interactions with models happen via CRUD operations.

info

👉 In DartWay, Create and Update are unified into a single save operation.
This keeps the API smaller, easier to use, and AI-friendly.

CRUD-only API guarantees:

  • Features remain consistent.
  • Data layer is unified across Flutter and Server.
  • AI assistants can scaffold features reliably.

Flutter (data layer)

  • saveModel
  • deleteModel
  • watchModel / readModel / watchMaybeModel / readMaybeModel
  • watchModelList / readModelList
info

read and watch both use the same server-side configuration
(GetModelConfig / GetListConfig).
The only difference is that watch subscribes to updates —
useful for state handling and automatic UI rebuilds.

note

Safe variants readMaybeModel / watchMaybeModel return null instead of throwing an error if the model is not found.

Server (configs)

  • SaveConfig
  • DeleteConfig
  • GetModelConfig
  • GetListConfig
info

On the server, you can define multiple GetModelConfig instances
for the same model with different filters or rules.

Examples:

  • One config returns only public profile fields, another includes private fields.
  • A config with a createIfMissing trigger to automatically create an entity if not found.

How to add advanced logic

Advanced logic should always extend the CRUD layer, not bypass it.
There are three recommended levels of extension:

1. Event Models

Use dedicated models to represent events that change base models.
Example: instead of updating UserProfile.balance directly,
create a BalanceEvent with fields like userId, amount, reason.

Event Models give you:

  • Safety → prevent concurrent update conflicts.
  • Traceability → every change is logged.
  • Consistency → one place for business rules (fees, limits, commissions).

2. CRUD API configuration

Extend CRUD behavior through configs (SaveConfig, DeleteConfig, GetModelConfig):

  • Validation → check inputs, enforce domain constraints.
  • Pre-processing → enrich or transform data before insert/update.
  • Post-processing → run logic after saving (e.g., recalc counters).
  • Side effects → trigger notifications, external service calls, etc.

All of these stay within the CRUD layer — no need for separate endpoints.

3. Custom endpoints (last resort)

In rare cases, create a dedicated endpoint.
Use this only when it cannot fit into CRUD patterns.

Examples:

  • File upload/download (often still represented as a model like FileUploadRequest)
  • External webhooks
  • Heavy async processing

Custom endpoints must be clearly documented as exceptions.


Why so strict?

Strict rules may feel limiting at first,
but they pay off with speed, reliability, and safety when the project grows.

  • Consistency → everyone knows where to look for logic.
  • Maintainability → refactors don’t break hidden actions.
  • AI-friendly → code generation always follows the same path.
  • Scalability → adding features doesn’t add chaos.

Rule of thumb

  • Every new feature = new model(s) + CRUD.
  • All Flutter ↔ Server interactions = CRUD operations only.
  • Avoid standalone action endpoints unless absolutely unavoidable.