Files
eryao/.trellis/spec/frontend/directory-structure.md
T

9.2 KiB

Directory Structure

How Flutter app code is organized in this project.


Overview

This Flutter app follows a feature-first architecture with clear separation of concerns:

  • Feature modules in features/ for bounded product capabilities
  • Core infrastructure in core/ for cross-feature protocols
  • Shared UI components in shared/ for reusable widgets
  • Data layer in data/ for infrastructure abstractions

Directory Layout

apps/lib/
├── main.dart                    # Only root entry file
├── app/                         # App bootstrap & DI
│   ├── di/                      # Dependency injection setup
│   ├── router.dart              # Route definitions
│   └── app.dart                 # App configuration
├── core/                        # Cross-feature infrastructure
│   ├── auth/                    # Session store, auth state
│   ├── config/                  # Env configuration
│   ├── logging/                 # Structured logging
│   └── network/                 # HTTP client, error mapping
├── data/                        # Shared infrastructure ONLY
│   ├── cache/                   # Cache implementations
│   ├── network/                 # Network adapters
│   └── storage/                 # Local storage
├── features/                    # Feature modules
│   ├── auth/                    # Authentication feature
│   ├── home/                    # Home feature
│   ├── divination/              # Divination feature
│   ├── settings/                # Settings feature
│   └── ...                      # Other features
├── shared/                      # Reusable UI components
│   ├── widgets/                 # Shared widgets
│   └── theme/                   # App theme, design tokens
└── l10n/                        # Localization

Module Organization

Feature Module Structure

Each feature follows consistent structure:

features/<feature>/
├── data/                        # Data layer
│   ├── apis/                    # API clients
│   ├── repositories/            # Repository implementations
│   ├── services/                # Feature-specific services
│   └── models/                  # Data models/DTOs
└── presentation/                # Presentation layer
    ├── bloc/                    # State management (BLoC/Cubit)
    ├── screens/                # Screen widgets
    └── widgets/                 # Feature-specific widgets

Example: features/auth/

features/auth/
├── data/
│   ├── apis/
│   │   └── auth_api.dart        # HTTP API calls
│   ├── repositories/
│   │   └── auth_repository.dart # Repository implementation
│   └── models/
│       ├── auth_user.dart       # User model
│       └── session_response.dart # Session DTO
└── presentation/
    ├── bloc/
    │   ├── auth_bloc.dart       # AuthBloc (ChangeNotifier)
    │   └── auth_state.dart      # AuthState
    └── screens/
        └── login_screen.dart    # Login screen widget

Core Module Structure

Core contains cross-feature infrastructure:

core/
├── auth/
│   └── session_store.dart       # Global session management
├── config/
│   └── env.dart                 # Environment configuration
├── logging/
│   ├── logger.dart              # Logger interface
│   ├── log_service.dart         # LogService implementation
│   ├── log_entry.dart           # Log entry model
│   └── error_handler.dart       # Global error handler
└── network/
    ├── api_problem.dart         # RFC7807 error model
    └── api_problem_mapper.dart  # Error mapping

Shared Widget Structure

Shared contains reusable UI components:

shared/
├── widgets/
│   ├── app_banner.dart          # App-wide banner
│   ├── app_loading_indicator.dart # Loading indicator
│   ├── bottom_nav_bar.dart      # Navigation bar
│   └── divination/              # Divination domain widgets
│       ├── gua_icon.dart        # Gua icon widget
│       ├── yao_glyph.dart       # Yao glyph widget
│       └── ...
└── theme/
    ├── app_theme.dart           # Theme definition
    └── design_tokens.dart       # Spacing, radius, colors

Placement Rules

Where to Put Code

Code Type Location Reason
Feature business logic features/<feature>/ Bounded context
Cross-feature protocol core/ Shared by multiple features
Reusable UI widget shared/widgets/ Reusable by multiple screens
Infrastructure abstraction data/ Cache/network/storage
Feature repository/model features/<feature>/data/ Feature-scoped data

Decision Tree

Is it feature-specific business logic?
  → Yes: features/<feature>/
  → No: Is it reusable UI?
    → Yes: shared/widgets/
    → No: Is it infrastructure?
      → Yes: data/
      → No: Is it cross-feature protocol?
        → Yes: core/
        → No: Re-evaluate

Forbidden Patterns

Do NOT:

  1. Place feature business repositories in data/

    • Wrong: data/repositories/auth_repository.dart
    • Right: features/auth/data/repositories/auth_repository.dart
  2. Create directories under lib/ other than allowed second-level

    • Wrong: lib/utils/, lib/helpers/, lib/constants/
    • Right: Use core/, shared/, or feature-specific locations
  3. Put feature-specific UI in shared/widgets/

    • Wrong: shared/widgets/auth_login_form.dart
    • Right: features/auth/presentation/widgets/login_form.dart
  4. Import feature data layer from other features

    • Wrong: import 'package:app/features/auth/data/repositories/auth_repository.dart' in features/home/
    • Right: Access via app-level facade or DI

Naming Conventions

Files

  • snake_case: auth_bloc.dart, login_screen.dart
  • Feature prefix for shared: app_loading_indicator.dart, app_banner.dart

Classes

  • PascalCase: AuthBloc, AuthState, LoginScreen
  • Suffixes:
    • *Bloc / *Cubit - State management
    • *Repository - Data access
    • *Api - HTTP clients
    • *Service - Business services
    • *Screen - Screen widgets
    • *Widget - Reusable widgets

Directories

  • Plural for collections: screens/, widgets/, models/
  • Singular for features: auth/, home/, divination/

Examples

Well-organized Feature: features/divination/

features/divination/
├── data/
│   ├── apis/
│   │   └── divination_api.dart          # HTTP API
│   ├── repositories/
│   │   └── divination_repository.dart    # Repository
│   ├── services/
│   │   └── voice_recorder.dart           # Feature service
│   └── models/
│       ├── divination_result.dart        # Domain model
│       ├── divination_params.dart        # Request params
│       └── follow_up_message.dart        # Message model
└── presentation/
    └── screens/
        ├── divination_screen.dart        # Main screen
        ├── auto_divination_screen.dart   # Auto mode
        ├── manual_divination_screen.dart # Manual mode
        └── follow_up_chat_screen.dart     # Follow-up chat

Shared Widget: shared/widgets/app_loading_indicator.dart

import 'package:flutter/material.dart';

class AppLoadingIndicator extends StatelessWidget {
  const AppLoadingIndicator({super.key});

  @override
  Widget build(BuildContext context) {
    return const CircularProgressIndicator();
  }
}

Core Infrastructure: core/logging/logger.dart

import 'log_service.dart';

class Logger {
  final String module;

  Logger(this.module, this._service);

  static void setLogService(LogService service) {
    _globalLogService = service;
  }

  void error({
    required String message,
    required Object error,
    required StackTrace stackTrace,
  }) {
    _service!.error(
      message: message,
      error: error,
      stackTrace: stackTrace,
      module: module,
    );
  }
}

Logger getLogger(String module) => Logger.get(module);

Key Principles

Directory Contract (Must)

  1. Only allowed second-level directories: app/, core/, data/, features/, shared/, l10n/
  2. Only one root entry: lib/main.dart
  3. No ad-hoc directories: No utils/, helpers/, constants/ under lib/
  4. Feature isolation: Features should not import each other's data layer

Layer Boundaries

  1. PresentationData (via Repository interface)
  2. DataCore/Infrastructure (via DI)
  3. CoreNothing (foundation layer)
  4. SharedCore (for utilities)
  5. FeatureCore + Shared (for cross-cutting concerns)

Data Layer Boundary (Must)

  • data/ = infrastructure abstractions (cache/network/storage)
  • features/<feature>/data/ = feature business repositories/models
  • NEVER mix these boundaries