Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Best practices for VGV layered monorepo architecture in Flutter.
when_to_use
Use when structuring a multi-package Flutter app, creating data or repository packages, defining layer boundaries, or wiring dependencies between packages.
The data layer handles all external communication. Each data package wraps a single external source (REST API, local database, platform plugin) and exposes typed methods and response models.
Rules:
Models represent the external data shape — match the API/storage schema exactly
No Flutter imports — use the very_good_cli MCP server create dart_package tool
Constructor-inject HTTP clients for testability
Response models use fromJson / toJson factories
Export everything through a barrel file — never expose src/
Pattern: Data Client Class
Constructor-inject the HTTP client for testability. Return typed response models — never raw JSON.
/// HTTP client for the User API.
class UserApiClient {
// http.Client injected — tests pass a mock, production gets a real client
UserApiClient({
required String baseUrl,
http.Client? httpClient,
}) : _baseUrl = baseUrl,
_httpClient = httpClient ?? http.Client();
final String _baseUrl;
final http.Client _httpClient;
/// Every method returns a typed response model.
Future<UserResponse> getUser(String userId) async {
final response = await _httpClient.get(
Uri.parse('$_baseUrl/users/$userId'),
);
if (response.statusCode != 200) {
throw UserApiException(response.statusCode, response.body);
}
return UserResponse.fromJson(
json.decode(response.body) as Map<String, dynamic>,
);
}
}
See worked-example.md for the complete user_api_client package with pubspec, barrel files, response models, and exception class.
Repository Layer
The repository layer orchestrates data sources and exposes domain models. Each repository composes one or more data clients, transforms response models into domain models, and provides a clean API for the business logic layer.
Rules:
No inter-repository dependencies — repositories are isolated
No Flutter SDK — the very_good_cli MCP server create dart_package tool
Domain models live in the repository package — not in data packages
Transform data models into domain models — never leak API response shapes upstream
Accept all data clients via constructor injection
Pattern: Domain Model + Repository Transformation
Domain models extend Equatable and represent the app's internal data shape — distinct from the API response shape. The repository method transforms between them.
/// Domain model — lives in the repository package, NOT the data package.
/// Fields match the app's needs, not the API schema.
class User extends Equatable {
const User({
required this.id,
required this.email,
required this.displayName,
this.avatarUrl,
});
final String id;
final String email;
final String displayName;
final String? avatarUrl;
@override
List<Object?> get props => [id, email, displayName, avatarUrl];
}
/// Repository accepts data client via constructor — never creates its own.
class UserRepository {
const UserRepository({
required UserApiClient userApiClient,
}) : _userApiClient = userApiClient;
final UserApiClient _userApiClient;
/// Transforms UserResponse (API shape) → User (domain shape).
Future<User> getUser(String userId) async {
final response = await _userApiClient.getUser(userId);
return User(
id: response.id,
email: response.email,
displayName: response.displayName,
avatarUrl: response.avatarUrl,
);
}
}
See worked-example.md for the complete user_repository package with pubspec, barrel files, and error handling. See model-transformation.md for detailed transformation patterns between data and domain models.
Dependency Graph
Each layer's pubspec.yaml enforces the architecture through path dependencies.
Data Package (packages/user_api_client/pubspec.yaml)
dependencies:# External packages only — no local dependencieshttp:^1.4.0json_annotation:^4.9.0
dependencies:equatable:^2.0.7# Path dependency on data layer packageuser_api_client:path:../user_api_client
Root App (pubspec.yaml)
dependencies:flutter:sdk:flutterflutter_bloc:^9.1.0# Repository packages only — data packages are transitiveauth_repository:path:packages/auth_repositoryuser_repository:path:packages/user_repository
The app never depends on data packages directly. Data packages are transitive dependencies through repositories. This enforces the layer boundary — business logic and presentation cannot bypass the repository layer.
Data Flow
Step-by-step walkthrough: user taps "Load Profile" button.
See data-flow.md for the full data flow walkthrough with code at each layer.
App Bootstrap
The app's main_<flavor>.dart creates all data clients and repositories, then passes them to the App widget. MultiRepositoryProvider makes repositories available to the entire widget tree.
Flavors change only the configuration (base URLs, API keys) — the architecture stays identical across development, staging, and production. See worked-example.md for the App widget with MultiRepositoryProvider.
Anti-Patterns
Anti-Pattern
Problem
Correct Approach
Widget calls API client directly
Bypasses Repository and Business Logic layers — no transformation, no state management