project-structure-and-packages
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseProject structure & packages
项目结构与 package
The layout is a feedback loop, not decoration: structure exists so that "what imports Flutter?", "what has no test?", and "does this feature reach into another?" are answerable by and , not by trust. Default to one Flutter package (the app), organised feature-first under over a shared foundation. Reach for a second, pure-Dart package only when a body of logic earns the isolation. A is the audit artifact — a package cannot import what it does not declare, so the dependency list is the layer boundary.
greplslib/features/pubspec.yamlThis skill OWNS the physical directory tree (D1). Other skills reference it rather than drawing their own.
Read the reference for the task at hand:
- — the default one-package feature-first tree, the shared-foundation folders, placement-by-failure, the deliberately-absent list.
references/single-package-layout.md - — the downward-only DAG, feature isolation, pubspec-as-audit, the compile-firewall insight.
references/layering-and-dependencies.md - — when (and how) to extract a pure-Dart package: barrel-over-src,
references/workspace-and-packages.md, naming the core generically.resolution: workspace
Run and before a PR. The purity check auto-detects only pure roots named ; a pure package with a plain generic name (, ) must be passed explicitly, e.g. .
scripts/check_structure.shscripts/check_import_boundaries.sh*_coremoneyschedulingPURE_DIR=packages/money scripts/check_import_boundaries.sh目录布局是一种反馈闭环,而不是装饰:建立结构,是为了让“哪些代码导入了 Flutter?”“哪些内容没有测试?”“这个 feature 是否伸入了另一个 feature?”这些问题能够通过 和 回答,而不是依赖信任。默认采用一个 Flutter package(应用本身),在 shared foundation 之上,以 feature-first 方式组织到 下。只有当某一部分逻辑确实值得隔离时,才引入第二个 pure-Dart package。 是 audit artifact——package 无法导入未声明的依赖,因此依赖列表就是layer boundary。
greplslib/features/pubspec.yaml此 skill 拥有物理目录树(D1)的所有权。其他 skill 引用该目录树,而不是各自定义一套。
针对当前任务阅读参考文档:
- ——默认的单 package、feature-first 目录树,shared-foundation 目录,按 failure 归属放置对象,以及有意缺省的目录清单。
references/single-package-layout.md - ——向下单向 DAG、feature isolation、pubspec audit 以及 compile-firewall 机制。
references/layering-and-dependencies.md - ——何时以及如何提取 pure-Dart package:barrel-over-src、
references/workspace-and-packages.md,以及 core 的通用命名方式。resolution: workspace
在提交 PR 前运行 和 。purity check 只会自动检测名称以 结尾的 pure root;如果 pure package 使用普通通用名称(、),则必须显式传入,例如 。
scripts/check_structure.shscripts/check_import_boundaries.sh*_coremoneyschedulingPURE_DIR=packages/money scripts/check_import_boundaries.shNon-negotiable rules
不可协商的规则
- Single package by default. A small-to-medium app is one Flutter package; its is committed (it is an app, not a library). Do not add
pubspec.lock, Melos, or a Dart workspace until a body of pure logic genuinely earns isolation (seepackages/). Multiplying packages before there is anything to multiply buys nothing and costs days.references/workspace-and-packages.md - Feature-first inside , over a shared foundation. Each screen/surface is a folder under
lib/features/(lib/features/<feature>/+presentation/; optional<feature>_notifier.dart,application/, rarelydomain/). The cross-cutting foundation splits by technical role at the top level —data/(pure),core/(repositories + DB),data/(side-effect ports),services/,routing/,theme/— because those files serve every feature.l10n/is a thin entry that callsmain.dart;bootstrap()is the composition root;bootstrap.dartbuildsapp.dart.MaterialApp.router - No grab-bags, no unnamed nesting. There is no hard depth cap — feature-first naturally nests (). The rule is that every folder has a named responsibility: no
features/orders/presentation//utils//helpers//common/, no grab-bagmisc/.shared/is the SANCTIONED pure-foundation home, not a junk drawer.core/ - Place a class in the layer whose failure it owns. Talks to the DB → . Talks to a platform channel →
data/. A pure value type / calculator / the Clock seam →services/native/(or a feature'score/if truly feature-local). Everything else → the feature that renders it. This is a deterministic rule, not taste.domain/ - is the audit artifact — declare the audit-minimal dependency set. An unused dependency is a review reject: it muddies the claim the manifest makes. The narrowest dep list keeps each layer boundary provable by reading one file.
pubspec.yaml - Dependencies point one way, downward only; features never import features. . No lower layer imports an upper one; no shared code imports the composition root. A feature folder NEVER imports another feature folder — share via
features → services/data → core/core/dataor navigate by route (seeservices). A cycle is a design defect: the analyzer resolves the whole graph in one context, so an upward or cross-feature import is an error, not a runtime surprise.navigation-and-routing - A pure-Dart core depends on only — the missing Flutter import is a compile firewall. In-app,
metaholds only pure foundation (value objects,lib/core//Result, the Clock seam, calculators) and reads no wall clock. Time comes fromFailure'spackage:clock(ambientClock, or aclockparam), neverClock, never a bespokeDateTime.now()— seeClockService. When such logic is extracted to a package, the empty-of-Flutter manifest makes a strayservice-boundary-and-nativeorWidgeta build error.DateTime.now() - One public barrel over a private — for PACKAGES only. A shared package exposes exactly one library
lib/src/that exports the stable surface; everything else lives underlib/<package>.dart, which no other package may import. The app package uses neither barrels norlib/src/: it has no external importers, and barrels add analyzer cost plus circular-import risk for zero benefit.lib/src/ - No junk-drawer folders. No ,
utils/,helpers/,common/, or a grab-bagmisc/. A helper either lives beside the type it serves (a same-file extension) or is a named service/value type with a real home.shared/is explicitly NOT a junk drawer — it is the named pure-foundation layer.core/ - Tests mirror 1:1.
lib/fortest/data/order_repository_test.dart. "What has no test?" then reduces to a diff of twolib/data/order_repository.dartoutputs. Cross-cutting assertions that belong to no single file get their own folder (ls); shared fakes go intest/policy/.test/support/ - One primary declaration per file; filename = that declaration in .
snake_caseholdsorder_repository.dart. The one exception: aOrderRepositoryhierarchy lives in a single file, because that file is the closed set the compiler enforces.sealed - Package imports everywhere; never mix with relative. . Mixing
always_use_package_importsand relative paths lets the same member resolve two ways, producing two distinct runtime types. Package imports also survive file moves and are greppable.package:
- 默认使用单 package。 小型到中型应用只使用一个 Flutter package;其 必须提交到版本控制(它是 app,而不是 library)。不要添加
pubspec.lock、Melos 或 Dart workspace,除非某部分 pure logic 确实值得隔离(参见packages/)。在还没有可拆分内容之前增加 package 只会一无所获,却会耗费数日。references/workspace-and-packages.md - 在 内采用 feature-first,而不是按 shared foundation 组织。 每个 screen/surface 都是
lib/features/下的一个目录(lib/features/<feature>/+presentation/;可选<feature>_notifier.dart、application/,很少使用domain/)。跨 feature 的 shared foundation 在顶层按技术角色拆分——data/(pure)、core/(repository + DB)、data/(side-effect port)、services/、routing/、theme/——因为这些文件会服务所有 feature。l10n/是调用main.dart的精简入口;bootstrap()是 composition root;bootstrap.dart构建app.dart。MaterialApp.router - 禁止杂物袋和未命名嵌套。 目录深度没有硬性上限——feature-first 自然会产生嵌套结构()。规则在于每个目录都有明确命名的职责:不得使用
features/orders/presentation/、utils/、helpers/、common/,也不得使用 grab-bagmisc/。shared/是唯一获准的 pure-foundation 目录,而不是 junk drawer。core/ - 将类放在其负责 failure 类型的 layer。 与 DB 通信 → 。与 platform channel 通信 →
data/。Pure value type、calculator 或 Clock seam →services/native/(如果确实仅属于某个 feature,也可以放在该 feature 的core/)。其他所有内容 → 负责渲染它的 feature。这是一条确定性规则,而不是凭个人偏好决定。domain/ - 是 audit artifact——声明满足审计的最小依赖集。 未使用的依赖会直接导致审查拒绝:它会模糊 manifest 所表达的声明。最窄的依赖列表可以让你仅通过阅读一个文件就证明各 layer boundary。
pubspec.yaml - 依赖只能单向指向下方;feature 之间绝不互相导入。 。低层不得导入高层;shared code 不得导入 composition root。Feature 目录绝不导入另一个 feature 目录——应通过
features → services/data → core/core/data共享,或者通过 route 进行导航(参见services)。Cycle 属于设计缺陷:analyzer 会在同一个 context 中解析整个 graph,因此向上导入或跨 feature 导入是错误,而不只是运行时才会出现的意外。navigation-and-routing - Pure-Dart core 仅依赖 ——缺少 Flutter import 就构成 compile firewall。 在 app 内,
meta只存放 pure foundation(value object、lib/core//Result、Clock seam、calculator),并且不读取 wall clock。时间来自Failure的package:clock(ambientClock,或clock参数),绝不能使用Clock,也不能创建定制的DateTime.now()——参见ClockService。当这类逻辑被提取为 package 时,不含 Flutter 的 manifest 会让误用的service-boundary-and-native或Widget直接触发 build error。DateTime.now() - 一个公共 barrel 覆盖私有 ——仅适用于 PACKAGES。 Shared package 只暴露一个 library
lib/src/,用于导出稳定 API;其他所有内容都位于lib/<package>.dart下,任何其他 package 都不得导入。App package 两者都不使用:barrel 和lib/src/:它没有外部 importer,而 barrel 会增加 analyzer cost 和 circular-import risk,却没有带来任何收益。lib/src/ - 禁止 junk-drawer 目录。 不得使用 、
utils/、helpers/、common/或 grab-bagmisc/。Helper 要么放在其服务类型的旁边(例如同文件 extension),要么作为具有明确归属的命名 service/value type。shared/明确不是 junk drawer——它是经过命名的 pure-foundation layer。core/ - 测试与 一一对应。
lib/对应test/data/order_repository_test.dart。这样,“哪些内容没有测试?”就简化为比较两次lib/data/order_repository.dart输出的差异。不属于任何单个文件的跨领域断言放入独立目录(ls);shared fake 放入test/policy/。test/support/ - 每个文件只有一个主要声明;文件名等于该声明并使用 。
snake_case保存order_repository.dart。唯一的例外是OrderRepositoryhierarchy,因为该文件本身就是编译器强制约束的封闭集合。sealed - 所有位置都使用 package import;绝不能混用相对 import。 启用 。混用
always_use_package_imports和相对路径会让同一个成员通过两种方式解析,产生两个不同的 runtime type。Package import 在文件移动后依然有效,而且便于 grep。package:
Default single-package tree
默认单 package 目录树
├── analysis_options.yaml # the lint safety net (see lint-and-style-config)
├── pubspec.yaml # pubspec.lock is COMMITTED — this is an app
├── lib/
│ ├── main.dart # thin entry: runs bootstrap(). Nothing else.
│ ├── bootstrap.dart # composition root: build infra, override providers, error net, runApp (see app-startup-and-bootstrap)
│ ├── app.dart # MaterialApp.router, theme wiring, ProviderScope child
│ ├── core/ # PURE foundation, NO Flutter: value objects, Result/Failure, Clock seam, calculators
│ ├── data/ # shared data layer: Drift db, DAOs, repositories, row→model mappers
│ ├── services/ # injectable side-effect ports + impls; native/ quarantines MethodChannel
│ │ └── native/
│ ├── routing/ # the single go_router config, typed routes, guards (see navigation-and-routing)
│ ├── theme/ # ThemeExtension token sets, ColorScheme (see design-system-structure)
│ ├── l10n/ # generated AppLocalizations + ARB (see i18n-rtl-l10n)
│ └── features/ # BY FEATURE — one folder per surface
│ └── orders/
│ ├── presentation/
│ │ ├── orders_screen.dart
│ │ ├── orders_notifier.dart # the ViewModel: a Riverpod Notifier (see state-management-riverpod)
│ │ └── widgets/
│ │ └── order_tile.dart
│ ├── application/ # OPTIONAL: use-cases spanning repositories
│ └── domain/ # OPTIONAL: feature-local models
├── test/ # mirrors lib/ 1:1
└── integration_test/ # only what unit/widget tests cannot reachA feature's folder is usually ABSENT — features read the shared repositories in . See for the full tree and the deliberately-absent list.
data/lib/data/references/single-package-layout.md├── analysis_options.yaml # the lint safety net (see lint-and-style-config)
├── pubspec.yaml # pubspec.lock is COMMITTED — this is an app
├── lib/
│ ├── main.dart # thin entry: runs bootstrap(). Nothing else.
│ ├── bootstrap.dart # composition root: build infra, override providers, error net, runApp (see app-startup-and-bootstrap)
│ ├── app.dart # MaterialApp.router, theme wiring, ProviderScope child
│ ├── core/ # PURE foundation, NO Flutter: value objects, Result/Failure, Clock seam, calculators
│ ├── data/ # shared data layer: Drift db, DAOs, repositories, row→model mappers
│ ├── services/ # injectable side-effect ports + impls; native/ quarantines MethodChannel
│ │ └── native/
│ ├── routing/ # the single go_router config, typed routes, guards (see navigation-and-routing)
│ ├── theme/ # ThemeExtension token sets, ColorScheme (see design-system-structure)
│ ├── l10n/ # generated AppLocalizations + ARB (see i18n-rtl-l10n)
│ └── features/ # BY FEATURE — one folder per surface
│ └── orders/
│ ├── presentation/
│ │ ├── orders_screen.dart
│ │ ├── orders_notifier.dart # the ViewModel: a Riverpod Notifier (see state-management-riverpod)
│ │ └── widgets/
│ │ └── order_tile.dart
│ ├── application/ # OPTIONAL: use-cases spanning repositories
│ └── domain/ # OPTIONAL: feature-local models
├── test/ # mirrors lib/ 1:1
└── integration_test/ # only what unit/widget tests cannot reachFeature 的 目录通常不存在——feature 会读取 中的 shared repository。完整目录树和有意缺省的目录清单见 。
data/lib/data/references/single-package-layout.mdPlacement decision procedure
放置对象决策流程
dart
// Q: where does OrderRepository go? A: it owns DB failure → lib/data/
class OrderRepository {
OrderRepository(this._db);
final AppDatabase _db;
// Rows never escape this class; the UI is owed a value type, not a raw row.
Future<List<Order>> loadAll() async {
final rows = await _db.selectOrders(); // package:drift confined here
return rows.map(Order.fromRow).toList();
}
}dart
// Q: where does PaymentChannel go? A: it constructs a MethodChannel → lib/services/native/
// One `grep -rn MethodChannel lib/` outside services/native/ is a review failure.
class PaymentChannel {
static const _channel = MethodChannel('app/payment');
Future<bool> canPay() async => await _channel.invokeMethod('canPay') ?? false;
}- A widget or model reused by 2+ features moves DOWN to (pure) or
lib/core/, or up to a named shared-widget home — never into one feature's folder that another then imports.lib/data/ - A new surface gets its own folder under even for a single file — consistency beats saving a line. Register its route into
lib/features/(seerouting/,scaffold-feature-module).navigation-and-routing - Read platform/accessibility state () in the widget at build time, not via a helper file or a provider —
MediaQuery.textScalerOfis already reactive. App state via Riverpod; platform state viaMediaQuery. SeeBuildContext.accessibility-as-code
dart
// Q: where does OrderRepository go? A: it owns DB failure → lib/data/
class OrderRepository {
OrderRepository(this._db);
final AppDatabase _db;
// Rows never escape this class; the UI is owed a value type, not a raw row.
Future<List<Order>> loadAll() async {
final rows = await _db.selectOrders(); // package:drift confined here
return rows.map(Order.fromRow).toList();
}
}dart
// Q: where does PaymentChannel go? A: it constructs a MethodChannel → lib/services/native/
// One `grep -rn MethodChannel lib/` outside services/native/ is a review failure.
class PaymentChannel {
static const _channel = MethodChannel('app/payment');
Future<bool> canPay() async => await _channel.invokeMethod('canPay') ?? false;
}- 被 2 个及以上 feature 复用的 widget 或 model 应向下移动到 (pure)或
lib/core/,或者向上移动到命名明确的 shared-widget 目录;绝不能放在某个 feature 的目录中再由其他 feature 导入。lib/data/ - 新 surface 即使只有一个文件,也要在 下拥有自己的目录——一致性比节省一行代码更重要。将其 route 注册到
lib/features/(参见routing/、scaffold-feature-module)。navigation-and-routing - 在 widget build 时读取 platform/accessibility state(),不要通过 helper 文件或 provider 获取——
MediaQuery.textScalerOf本身已经是 reactive。App state 使用 Riverpod;platform state 使用MediaQuery。参见BuildContext。accessibility-as-code
When to extract a package (multi-package / workspace)
何时提取 package(multi-package / workspace)
Extract a pure-Dart package the moment a body of logic satisfies all of: it is genuinely UI-free, it is worth testing headlessly in milliseconds, and the "imports no Flutter" guarantee is worth making a build error. Typical: money/units math, entities, deterministic algorithms. Below that bar it stays in .
lib/core/yaml
undefined当某部分逻辑同时满足以下所有条件时,应立即提取为 pure-Dart package:它真正不依赖 UI;它值得进行毫秒级的 headless testing;并且值得将“imports no Flutter”的保证变成 build error。典型例子包括 money/unit 计算、entity 和确定性 algorithm。未达到这一标准的逻辑应继续保留在 中。
lib/core/yaml
undefinedpackages/domain_core/pubspec.yaml — the manifest IS the purity audit
packages/domain_core/pubspec.yaml — the manifest IS the purity audit
name: domain_core # a GENERIC name — never the brand, so a rename never churns the core
publish_to: none
resolution: workspace # member of the root workspace (modern successor to path:/overrides)
environment:
sdk: ^3.6.0
dependencies:
meta: ^1.15.0 # the ONLY dependency. No flutter, no dart:io, no dart:ui, no clock.
dev_dependencies:
test: ^1.25.0 # plain — NOT flutter_test
dart test
The root `pubspec.yaml` lists members under `workspace:`; there is **one** committed `pubspec.lock` at the root. The public barrel `lib/domain_core.dart` exports the stable surface; internals stay under `lib/src/`. Full ritual, dependency matrix, and gates in `references/workspace-and-packages.md`.
> **When multi-package (workspace) only.** Everything in this section — `packages/`, `resolution: workspace`, barrels, `lib/src/` — applies solely once you have decided to split. A single-package app does none of it.name: domain_core # a GENERIC name — never the brand, so a rename never churns the core
publish_to: none
resolution: workspace # member of the root workspace (modern successor to path:/overrides)
environment:
sdk: ^3.6.0
dependencies:
meta: ^1.15.0 # the ONLY dependency. No flutter, no dart:io, no dart:ui, no clock.
dev_dependencies:
test: ^1.25.0 # plain — NOT flutter_test
dart test
根 `pubspec.yaml` 在 `workspace:` 下列出 member;根目录只提交**一个** `pubspec.lock`。公共 barrel `lib/domain_core.dart` 导出稳定 API;内部实现保留在 `lib/src/` 下。完整规范流程、依赖矩阵和门禁见 `references/workspace-and-packages.md`。
> **仅适用于 multi-package(workspace)。** 本节中的所有内容——`packages/`、`resolution: workspace`、barrels、`lib/src/`——仅在你决定拆分之后才适用。单 package 应用不采用其中任何一项。Anti-patterns
反模式
- Splitting the shared foundation feature-first (,
orders/each with their own DB + repositories). The entity graph is shared; keep repositories/DB insettings/and pure types inlib/data/. Feature-first applies insidelib/core/, not to the data graph.lib/features/ - One feature folder importing another feature folder. It couples two surfaces that should meet only via /
core/dataor a route. Lift the shared piece down, or navigate.services - ,
utils/,helpers/,common/, or a grab-bagmisc/. A junk drawer; every candidate belongs beside its owner or as a named type. (shared/is fine — it is the named pure-foundation layer.)core/ - Barrels or in the single app package. No external importer justifies them; they add analyzer cost and circular-import risk.
lib/src/is a PACKAGE convention only.lib/src/ - Constructing a inside a widget or repository. It cannot be faked and cannot be found in review. Quarantine it in
MethodChannel.services/native/ - Adding ,
flutter, ordart:iotoDateTime.now()or a pure core package. It destroys the compile firewall and the headless test tier; inject thelib/core/(Clock) and seed instead.package:clock - +
path:instead ofdependency_overrides. Defeats single-context resolution and hides the version clashes a workspace exists to surface.resolution: workspace - Naming the core package after the brand (). A future rename churns every import. Name it for what it holds (
myapp_core,domain_core).money - A speculative dependency "for later." An unused edge muddies the audit and is a review reject.
- Mixing and relative imports. Two import paths → two runtime types for one class.
package:
- 按 feature-first 拆分 shared foundation(、
orders/各自拥有 DB 和 repository)。Entity graph 是共享的;repository 和 DB 应保留在settings/,pure type 应保留在lib/data/。Feature-first 适用于lib/core/内部,而不适用于 data graph。lib/features/ - 一个 feature 目录导入另一个 feature 目录。 这会耦合两个本应只通过 /
core/data或 route 交互的 surface。应将共享部分向下提取,或者通过导航交互。services - 使用 、
utils/、helpers/、common/或 grab-bagmisc/。 这些都是 junk drawer;每个候选对象都应放在其 owner 旁边,或作为具有明确归属的命名 type。(shared/没有问题——它是经过命名的 pure-foundation layer。)core/ - 在单 app package 中使用 barrels 或 。 没有外部 importer 来证明其必要性;它们只会增加 analyzer cost 和 circular-import risk。
lib/src/仅是 PACKAGE 约定。lib/src/ - 在 widget 或 repository 内构造 。 它无法被 fake,也无法在审查时准确定位。必须将其隔离在
MethodChannel中。services/native/ - 向 或 pure core package 添加
lib/core/、flutter或dart:io。 这会破坏 compile firewall 和 headless test tier;应注入DateTime.now()(Clock)并提供 seed。package:clock - 使用 +
path:,而不是dependency_overrides。 这会破坏 single-context resolution,并隐藏 workspace 原本要暴露的版本冲突。resolution: workspace - 使用品牌名称命名 core package()。未来的重命名会 churn 所有 import。应根据其承载的内容命名(
myapp_core、domain_core)。money - 添加“以后可能用到”的 speculative dependency。 未使用的依赖边会模糊 audit,并直接导致审查拒绝。
- 混用 和相对 import。 两种 import 路径会让同一个 class 产生两个 runtime type。
package:
Definition of done
完成定义
- Default is a single Flutter package; committed; no
pubspec.lock/Melos/workspace unless a pure body of logic earned it.packages/ - Top level is the shared foundation (); features live under
core/ data/ services/ routing/ theme/ l10n/;lib/features/<feature>/only runsmain.dart.bootstrap() - No feature folder imports another feature folder; sharing happens via /
core/dataor a route.services - Every class sits in the layer whose failure it owns; only in
MethodChannel; rows never escapeservices/native/; pure foundation and the Clock seam indata/.core/ - No /
utils//helpers//common//grab-bag-misc/folder anywhere;shared/is present as the named pure-foundation layer, not a catch-all.core/ - No or barrel in the app package (they are PACKAGE conventions only).
lib/src/ - mirrors
test/1:1; shared fakes inlib/, cross-cutting checks intest/support/.test/policy/ - One primary declaration per file, filename = declaration in (sealed hierarchy the sole exception);
snake_case.always_use_package_imports - If a package was extracted: it is a member, generically named, with one public barrel over
resolution: workspace; a pure core declareslib/src/only (nometa/flutter/dart:io/clock); one rootdart:ui.pubspec.lock - Dependency graph points downward only; no cross-package import; deps are audit-minimal with no unused edge.
lib/src/ - and
scripts/check_structure.shpass.scripts/check_import_boundaries.sh
- 默认使用单个 Flutter package;提交 ;除非某部分 pure logic 确实值得隔离,否则不使用
pubspec.lock、Melos 或 workspace。packages/ - 顶层为 shared foundation(、
core/、data/、services/、routing/、theme/);feature 位于l10n/;lib/features/<feature>/只运行main.dart。bootstrap() - Feature 目录之间不相互导入;共享通过 /
core/data或 route 完成。services - 每个 class 都位于其负责 failure 类型的 layer;只出现在
MethodChannel;row 不得逃逸services/native/;pure foundation 和 Clock seam 位于data/。core/ - 任何位置都没有 、
utils/、helpers/、common/或 grab-bagmisc/目录;shared/作为经过命名的 pure-foundation layer 存在,而不是 catch-all。core/ - App package 中没有 或 barrel;它们仅是 PACKAGE 约定。
lib/src/ - 与
test/一一对应;shared fake 位于lib/,跨领域检查位于test/support/。test/policy/ - 每个文件只有一个主要声明,文件名等于声明名并使用 (
snake_casehierarchy 是唯一例外);启用sealed。always_use_package_imports - 如果提取了 package:它是 member,名称通用,使用一个公共 barrel 覆盖
resolution: workspace;pure core 仅声明lib/src/(不包含meta/flutter/dart:io/clock);根目录只有一个dart:ui。pubspec.lock - 依赖 graph 仅向下指向;不存在跨 package 的 import;依赖满足最小审计要求,不存在未使用的依赖边。
lib/src/ - 和
scripts/check_structure.sh检查通过。scripts/check_import_boundaries.sh
Related skills
相关 skill
- — the layered MVVM this folder shape realises (features-as-folders, foundations-as-packages, single write-path repositories); it keeps layer semantics and references this tree.
flutter-architecture - — the fixed anatomy of one
scaffold-feature-modulefolder (View + 1:1lib/features/<feature>/Notifier + scoped providers) and how it registers a route.<feature>_notifier.dart - — owns
app-startup-and-bootstrap/main.dart, the composition root ordering, and the global error net.bootstrap.dart - — owns
navigation-and-routing(go_router config, typed routes, guards) that features register into.lib/routing/ - — the role suffixes (
naming-conventions/Screen/Notifier/Repository/Service) and the file=primary-declaration rule referenced above.Gateway - — pubspec/lock mechanics, caret ranges, auditing a transitive tree before adding a dep.
dependency-hygiene - — per-package
codegen-and-toolchainscoping and the commit-vs-gitignore decision for generated code.build.yaml - — the
lint-and-style-configthat promotesanalysis_options.yamland silent-failure lints to errors.always_use_package_imports - — the
service-boundary-and-nativeMethodChannel quarantine, the injectable Service/Gateway ports, and theservices/native/-via-Clocktime seam.clockProvider - — the canonical resident of
value-objects-money-and-unitsor an extracted pure-Dart core.lib/core/
- ——实现该目录形态的分层 MVVM(features-as-folders、foundations-as-packages、single write-path repositories);它保留 layer 语义并引用此目录树。
flutter-architecture - ——定义单个
scaffold-feature-module目录的固定结构(View + 1:1lib/features/<feature>/Notifier + scoped provider),以及如何注册 route。<feature>_notifier.dart - ——负责
app-startup-and-bootstrap/main.dart、composition root 顺序和全局 error net。bootstrap.dart - ——负责
navigation-and-routing(go_router config、typed route、guard),feature 会向其中注册。lib/routing/ - ——定义角色后缀(
naming-conventions/Screen/Notifier/Repository/Service)以及上文引用的 file=primary-declaration 规则。Gateway - ——pubspec/lock 机制、caret range,以及添加依赖前审计 transitive tree。
dependency-hygiene - ——按 package 设置
codegen-and-toolchainscope,以及决定 generated code 应提交还是加入 gitignore。build.yaml - ——通过
lint-and-style-config将analysis_options.yaml和 silent-failure lint 提升为 error。always_use_package_imports - ——
service-boundary-and-nativeMethodChannel 隔离、可注入的 Service/Gateway port,以及通过services/native/获取时间的 Clock seam。clockProvider - ——位于
value-objects-money-and-units或已提取 pure-Dart core 中的标准 value object。lib/core/
References
参考资料
- Dart package layout: https://dart.dev/tools/pub/package-layout
- Dart & Flutter workspaces (): https://dart.dev/tools/pub/workspaces
resolution: workspace - Effective Dart — libraries & imports: https://dart.dev/effective-dart/usage#libraries
- pubspec format: https://dart.dev/tools/pub/pubspec
- Flutter architecture recommendations: https://docs.flutter.dev/app-architecture
- Dart package 布局:https://dart.dev/tools/pub/package-layout
- Dart 与 Flutter workspace():https://dart.dev/tools/pub/workspaces
resolution: workspace - Effective Dart——library 与 import:https://dart.dev/effective-dart/usage#libraries
- pubspec 格式:https://dart.dev/tools/pub/pubspec
- Flutter 架构建议:https://docs.flutter.dev/app-architecture