FOSMVVM Fluent DataModel Generator
Generate Fluent DataModels for server-side persistence following FOSMVVM architecture.
Dependency: This skill uses fosmvvm-fields-generator for the Fields layer (protocol, messages, YAML). Run that skill first for form-backed models.
Scope Guard
This skill is specifically for Fluent persistence layer (typically in Vapor server apps).
STOP and ask the user if:
- - The project doesn't use Fluent
- The target is iOS-only with CoreData, SwiftData, or Realm
- The user mentions a non-Fluent ORM or persistence layer
- You're unsure whether Fluent is the persistence layer
Check for Fluent indicators:
- -
Package.swift imports fluent, fluent-postgres-driver, fluent-sqlite-driver, etc. - Existing models use
@ID, @Field, @Parent, @Children, @Siblings property wrappers - A
Migrations/ directory exists with Fluent migration patterns - Imports include
FluentKit or INLINECODE11
If Fluent isn't present, inform the user: "This skill generates Fluent DataModels for server-side persistence. Your project doesn't appear to use Fluent. How would you like to proceed?"
When to Use This Skill
- - User asks to create a new model/entity/table
- User wants to add a database-backed type (Users, Ideas, Documents, etc.)
- User mentions needing CRUD operations for a new concept
- Creating the persistence layer for a new entity
Architecture Context
In FOSMVVM, the Model is the center - the source of truth that reads and writes flow through.
See FOSMVVMArchitecture.md | OpenClaw reference for full context.
DataModel in the Architecture
CODEBLOCK0
Fields vs DataModels
Fields protocol = Form input (user-editable subset)
- - What users type into forms
- Validation, labels, placeholders
- NO relationships, NO system-assigned fields
DataModel = Complete entity (Fluent implementation)
- - All fields including system-assigned (createdBy, timestamps)
- All relationships (@Parent, @Siblings, @Children)
- Fluent property wrappers, migrations, seeds
Not all entities need Fields:
- - Session: system auth, no user form → DataModel-only
- Audit records: system-generated → DataModel-only
- Junction tables: pure storage → DataModel-only
File Structure
Each form-backed model requires files across multiple targets:
CODEBLOCK1
How to Use This Skill
Invocation:
/fosmvvm-fluent-datamodel-generator
Prerequisites:
- - Model structure understood from conversation context
- Fields protocol exists (if form-backed model) via fosmvvm-fields-generator
- Relationships and system-assigned fields identified
- Fluent confirmed as the persistence layer
Workflow integration:
This skill is used for server-side persistence with Fluent. For form-backed models, run fosmvvm-fields-generator first to create the Fields protocol. The skill references conversation context automatically—no file paths or Q&A needed.
Pattern Implementation
This skill references conversation context to determine DataModel structure:
Model Type Detection
From conversation context, the skill identifies:
- - Entity purpose (user data, system records, audit logs, junction table)
- User input involvement (form-backed vs system-generated)
- Fields protocol requirement (if user edits this data)
Relationship Analysis
From requirements already in context:
- - One-to-many relationships (@Parent in DataModel, not in Fields)
- Many-to-many relationships (Junction table + @Siblings, NOT UUID arrays)
- Relationship naming (self-documenting names, not vague references)
Field Classification
Based on data source:
- - User-editable fields (from Fields protocol)
- System-assigned fields (createdBy, timestamps, status - DataModel only)
- Computed relationships (@Parent, @Children, @Siblings)
File Generation Order
If form-backed model (Fields protocol exists):
- 1. Fields layer already created via fosmvvm-fields-generator
- DataModel implementation referencing Fields
- Schema migration
- Seed data migration
- Tests
- Migration registration
If system-only model (no Fields):
- 1. DataModel struct
- Schema migration
- Seed data migration (if needed)
- Tests
- Migration registration
Design Validation
Before generating, the skill validates:
- 1. Form requirement - System-generated entities skip Fields
- Relationship patterns - Junction tables for many-to-many, @Parent for foreign keys
- Naming clarity - Relationships have self-documenting names
- Field separation - User fields in protocol, system fields in DataModel only
Context Sources
Skill references information from:
- - Prior conversation: Model requirements, relationships discussed
- Fields protocol: If Claude has read Fields protocol into context or just created it
- Database schema: From codebase analysis of existing models
- Migration patterns: From existing migrations in project
File Templates
See reference.md for complete file templates with all patterns.
Key Patterns
Fluent DataModel
CODEBLOCK2
Relationships (Associated Types Pattern)
PRINCIPLE: Existential types (any Protocol) are a code smell. Always ask "Is there any other way?" before using them.
For required relationships, use associated types in the protocol:
CODEBLOCK3
In the Fluent model, @Parent directly satisfies the protocol:
CODEBLOCK4
In schema: INLINECODE14
When to use each pattern:
- - Associated type (
associatedtype User: UserFields): Required relationships - Optional associated type: Not supported - use
ModelIdType? for optional FKs - Plain
ModelIdType: Optional FKs, external system references
Migrations
- - Schema migration named: INLINECODE18
- Seed migration named: INLINECODE19
- Seed is environment-aware (debug, test, release)
- Seed is idempotent: INLINECODE20
Raw SQL in Migrations (PostgreSQL Features)
For PostgreSQL-specific features (tsvector, LTREE, etc.), use SQLKit:
CODEBLOCK5
Key points:
- - Import
SQLKit (not just Fluent) - Cast database: INLINECODE23
- Use
SQLQueryString with \(unsafeRaw:) for identifiers - These columns are database-only (not in protocol or Fluent model)
Tests
- - Use
@Suite annotation with descriptive name - Conform to INLINECODE27
- Test all form fields
- Test validation with INLINECODE28
- Create private test struct implementing the Fields protocol
Test structs with associated types:
CODEBLOCK6
Naming Conventions
| Concept | Convention | Example |
|---|
| Model class | PascalCase singular | INLINECODE29 , INLINECODE30 |
| Table name |
snake_case plural |
users,
ideas |
| Field keys | snake_case |
created_at,
user_id |
| Enum cases | camelCase |
searchLanguage,
inProgress |
| Enum raw values | snake_case |
"search_language",
"in_progress" |
| Protocol |
{Model}Fields |
UserFields,
IdeaFields |
| Messages struct |
{Model}FieldsMessages |
UserFieldsMessages |
Common Field Types
| Swift Type | Fluent Type | Database |
|---|
| INLINECODE44 | INLINECODE45 | INLINECODE46 |
| INLINECODE47 |
.int |
INTEGER |
|
Bool |
.bool |
BOOLEAN |
|
Date |
.datetime |
TIMESTAMPTZ |
|
UUID |
.uuid |
UUID |
|
[UUID] |
.array(of: .uuid) |
UUID[] |
| Custom Enum |
.string |
VARCHAR (stored as raw value) |
|
JSONB |
.json |
JSONB |
See Also
Version History
| Version | Date | Changes |
|---|
| 1.0 | 2025-12-23 | Initial skill based on SystemConfig pattern |
| 1.1 |
2025-12-23 | Added relationship patterns (@Parent), initialization order, imports list |
| 1.2 | 2025-12-23 | Associated types for relationships (not existentials), raw SQL patterns, test struct patterns |
| 1.3 | 2025-12-24 | Factored out Fields layer to fields-generator skill |
| 2.0 | 2025-12-26 | Renamed to fosmvvm-fluent-datamodel-generator, added Scope Guard, generalized from Kairos-specific to FOSMVVM patterns, added architecture context |
| 2.1 | 2026-01-24 | Update to context-aware approach (remove file-parsing/Q&A). Skill references conversation context instead of asking questions or accepting file paths. |
FOSMVVM Fluent DataModel 生成器
按照 FOSMVVM 架构生成用于服务端持久化的 Fluent DataModel。
依赖: 本技能使用 fosmvvm-fields-generator 生成 Fields 层(协议、消息、YAML)。对于表单驱动的模型,请先运行该技能。
适用范围守卫
本技能专门用于 Fluent 持久化层(通常用于 Vapor 服务端应用)。
遇到以下情况请停止并询问用户:
- - 项目未使用 Fluent
- 目标仅为 iOS 端,使用 CoreData、SwiftData 或 Realm
- 用户提及非 Fluent 的 ORM 或持久化层
- 您不确定 Fluent 是否为持久化层
检查 Fluent 相关标识:
- - Package.swift 导入了 fluent、fluent-postgres-driver、fluent-sqlite-driver 等
- 现有模型使用了 @ID、@Field、@Parent、@Children、@Siblings 属性包装器
- 存在包含 Fluent 迁移模式的 Migrations/ 目录
- 导入语句包含 FluentKit 或 Fluent
如果未检测到 Fluent,请告知用户:本技能用于生成服务端持久化的 Fluent DataModel。您的项目似乎未使用 Fluent。请问您希望如何处理?
何时使用本技能
- - 用户要求创建新的模型/实体/表
- 用户想要添加数据库支持的类型(用户、想法、文档等)
- 用户提及需要为新概念实现 CRUD 操作
- 为新实体创建持久化层
架构上下文
在 FOSMVVM 中,Model 是核心——所有读写操作流经的事实来源。
完整上下文请参见 FOSMVVMArchitecture.md | OpenClaw 参考。
架构中的 DataModel
┌─────────────────────────────────────┐
│ Fluent DataModel │
│ (实现 Model + Fields) │
│ │
│ • 所有字段(系统 + 用户) │
│ • 关系(@Parent 等) │
│ • 时间戳、审计字段 │
│ • 持久化逻辑 │
└──────────────┬──────────────────────┘
│
┌────────────────────┼────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ ViewModelFactory│ │ CreateRequest │ │ UpdateRequest │
│ (投影器) │ │ RequestBody │ │ RequestBody │
│ │ │ │ │ │
│ → ViewModel │ │ → 持久化到 │ │ → 更新 │
│ (投影) │ │ DataModel │ │ DataModel │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Fields 与 DataModels 的区别
Fields 协议 = 表单输入(用户可编辑的子集)
- - 用户输入到表单的内容
- 验证、标签、占位符
- 不包含关系,不包含系统分配字段
DataModel = 完整实体(Fluent 实现)
- - 所有字段,包括系统分配字段(createdBy、时间戳)
- 所有关系(@Parent、@Siblings、@Children)
- Fluent 属性包装器、迁移、种子数据
并非所有实体都需要 Fields:
- - 会话:系统认证,无用户表单 → 仅 DataModel
- 审计记录:系统生成 → 仅 DataModel
- 连接表:纯存储 → 仅 DataModel
文件结构
每个表单驱动的模型需要跨多个目标创建文件:
── fosmvvm-fields-generator ──────────────────────────────────
{ViewModelsTarget}/ (共享协议层)
FieldModels/
{Model}Fields.swift ← 协议 + 枚举 + 验证
{Model}FieldsMessages.swift ← 本地化消息结构体
{ResourcesPath}/ (本地化资源)
FieldModels/
{Model}FieldsMessages.yml ← YAML 本地化字符串
── fosmvvm-fluent-datamodel-generator (本技能) ───────────────
{WebServerTarget}/ (服务端实现)
DataModels/
{Model}.swift ← Fluent 模型(实现协议)
Migrations/
{Model}+Schema.swift ← 表创建迁移
{Model}+Seed.swift ← 种子数据迁移
Tests/
{ViewModelsTarget}Tests/
FieldModels/
{Model}FieldsTests.swift ← 单元测试
database.swift ← 注册迁移
如何使用本技能
调用方式:
/fosmvvm-fluent-datamodel-generator
前置条件:
- - 通过对话上下文理解模型结构
- 如果为表单驱动模型,Fields 协议已通过 fosmvvm-fields-generator 创建
- 已识别关系和系统分配字段
- 已确认 Fluent 为持久化层
工作流集成:
本技能用于使用 Fluent 的服务端持久化。对于表单驱动模型,请先运行 fosmvvm-fields-generator 创建 Fields 协议。技能会自动引用对话上下文——无需文件路径或问答。
模式实现
本技能引用对话上下文来确定 DataModel 结构:
模型类型检测
通过对话上下文,技能识别:
- - 实体用途(用户数据、系统记录、审计日志、连接表)
- 用户输入参与度(表单驱动 vs 系统生成)
- Fields 协议需求(如果用户编辑此数据)
关系分析
根据上下文中已有的需求:
- - 一对多关系(DataModel 中使用 @Parent,Fields 中不使用)
- 多对多关系(连接表 + @Siblings,不使用 UUID 数组)
- 关系命名(自文档化名称,而非模糊引用)
字段分类
基于数据来源:
- - 用户可编辑字段(来自 Fields 协议)
- 系统分配字段(createdBy、时间戳、状态——仅 DataModel)
- 计算关系(@Parent、@Children、@Siblings)
文件生成顺序
如果是表单驱动模型(Fields 协议已存在):
- 1. Fields 层已通过 fosmvvm-fields-generator 创建
- 引用 Fields 的 DataModel 实现
- Schema 迁移
- 种子数据迁移
- 测试
- 迁移注册
如果是纯系统模型(无 Fields):
- 1. DataModel 结构体
- Schema 迁移
- 种子数据迁移(如果需要)
- 测试
- 迁移注册
设计验证
在生成之前,技能验证:
- 1. 表单需求 - 系统生成的实体跳过 Fields
- 关系模式 - 多对多使用连接表,外键使用 @Parent
- 命名清晰度 - 关系具有自文档化名称
- 字段分离 - 用户字段在协议中,系统字段仅在 DataModel 中
上下文来源
技能引用的信息来自:
- - 先前的对话:模型需求、讨论的关系
- Fields 协议:如果 Claude 已将 Fields 协议读入上下文或刚刚创建
- 数据库模式:通过代码库分析现有模型
- 迁移模式:通过项目中的现有迁移
文件模板
请参见 reference.md 获取包含所有模式的完整文件模板。
关键模式
Fluent DataModel
swift
import FluentKit
import FOSFoundation
import FOSMVVM
import FOSMVVMVapor
import Foundation
final class {Model}: DataModel, {Model}Fields, Hashable, @unchecked Sendable {
static let schema = {models} // 蛇形命名复数
@ID(key: .id) var id: ModelIdType?
// 来自协议的字段
@Field(key: field_name) var fieldName: FieldType
// 验证消息
let {model}ValidationMessages: {Model}FieldsMessages
// 时间戳
@Timestamp(key: created_at, on: .create) var createdAt: Date?
@Timestamp(key: updated_at, on: .update) var updatedAt: Date?
// 关键:首先初始化 validationMessages
init() {
self.{model}ValidationMessages = .init()
}
init(id: ModelIdType? = nil, fieldName: Field