bloomBUILD•SHIP•BLOOM
.md
Production Standards

Architectural Best Practices

Conventions followed by enterprise engineering teams building maintainable, high-throughput Dart backends and responsive Flutter frontends.

Full-Stack Project Structure

Bloom encourages a unified monorepo or full-stack layout where both the Dart backend and Flutter client share models, validation schemas, and protocol types without code duplication.

Directory Architecture (full-stack)
bash
my_fullstack_bloom_app/
├── bin/
│   ├── server.dart               # Production backend server entrypoint
│   └── worker.dart               # Background isolate task runner
├── lib/
│   ├── apps/                     # Modular server domain apps (Django-style)
│   │   ├── auth/
│   │   │   ├── models/           # Generated typed ORM models (User.dart)
│   │   │   ├── permissions.dart  # IsAuthenticated, IsStaff guards
│   │   │   ├── serializers.dart  # Model serializers & field sanitizers
│   │   │   ├── urls.dart         # Sub-router route definitions
│   │   │   └── views.dart        # Route handlers & DRF ViewSets
│   │   └── todos/
│   │       ├── models/           # TodoList, TodoTask models
│   │       ├── serializers.dart
│   │       ├── urls.dart
│   │       └── views.dart
│   ├── features/                 # Frontend client UI features (Flutter)
│   │   ├── auth/
│   │   │   ├── controllers/      # Signals-based AuthController
│   │   │   └── views/            # LoginScreen, RegisterScreen
│   │   └── todos/
│   │       ├── controllers/      # TodoQueryController, SWR cache
│   │       └── widgets/          # TaskListView, TodoItemCard
│   ├── routes/                   # Client filesystem navigation routes
│   └── main.dart                 # Flutter client application bootstrap
├── migrations/                   # SQL / Dart schema migration files
│   ├── auth/0001_initial.sql
│   └── todos/0001_initial.sql
└── bloom.yaml                    # Full-stack declarative configuration

Backend Architectural Rules

Key engineering principles for the Bloom Server ecosystem (bloom_db, bloom_rest, bloom_realtime, bloom_jobs):

Django-Style App Modularization

Organize backend domain features into self-contained modular directories containing models/, views.dart, serializers.dart, urls.dart, and permissions.dart.

Atomic Multi-Write Transactions

Never execute related database mutations sequentially on the base connection. Wrap multi-model operations inside db.transaction((tx) async { ... }) to guarantee atomic rollbacks.

Declarative Permission Composition

Compose endpoint authorization using reusable boolean algebra combinators like IsAuthenticated().and(IsOwnerOrAdmin()) rather than manual imperative if-checks in view handlers.

Deterministic QuerySet Building

Use type-safe QuerySet chains (filter, exclude, orderBy, limit) with generated ModelMeta rather than raw string SQL queries to ensure automatic SQL injection prevention and dialect portability.

Stateless Worker Payload Serialization

Pass lightweight scalar identifiers (e.g., {"user_id": 42}) in background job queues; allow worker isolates to fetch fresh database state inside their own isolated execution contexts.

Zero-Allocation Response Streaming

Stream large file uploads and binary assets directly through BloomStorageAdapter to S3/disk rather than buffering entire request bodies into RAM.

Transaction & Database Discipline

Always pass the transactional DbExecutor down to model queries and nested service calls to guarantee atomicity:

lib/apps/todos/services.dart
dart
// lib/apps/todos/services.dart - Atomic Transaction Best Practice
import 'package:bloom_db/bloom_db.dart';
import 'models/todo_task.dart';
import 'models/todo_list.dart';

class TodoService {
  final DbExecutor db;
  TodoService(this.db);

  Future<TodoTask> createTaskWithAudit({
    required int listId,
    required String title,
    required int userId,
  }) async {
    // Wrap related database writes in a transaction
    return await db.transaction((tx) async {
      // 1. Verify list exists and belongs to user using tx executor
      final list = await TodoListOrmExtension.objects()
          .filter({'id': listId, 'user_id': userId})
          .first(tx);
      
      if (list == null) {
        throw BloomOrmNotFoundError(model: 'TodoList');
      }

      // 2. Insert new task on tx
      final task = await TodoTask(
        listId: listId,
        title: title,
        isCompleted: false,
        createdAt: DateTime.now().toUtc(),
      ).save(tx);

      // 3. Atomically increment total tasks count on list
      await tx.execute(
        'UPDATE "todo_lists" SET "task_count" = "task_count" + 1 WHERE "id" = ?',
        [listId],
      );

      return task;
    });
  }
}

Client & UI Conventions

Feature-Driven Folder Structure

Group domain models, controllers, and specific widgets inside lib/features/<name>/, keeping lib/routes/ lightweight for page composition.

Never Call setState in Reactive Widgets

Use fine-grained Watch((context) => ...) wrappers around leaf nodes rather than making entire routes StatefulWidget.

Query Key Immutability

Construct query keys with deterministic lists like ['entity', id, filter] so cache hits and invalidations match reliably.

Controller Lifecycle Discipline

Instantiate signals with createSignal and avoid storing raw mutable variables outside of signals in controllers.

Idempotent Prebuild Runs

Never manually edit generated native manifests; declare permissions in bloom.yaml and run bloom prebuild in CI.