Back to list
hajimism

model-structure

by hajimism

0🍴 0📅 Jan 17, 2026

SKILL.md


name: model-structure description: Frontend model structure patterns. Use when creating a new model (e.g., user, product), adding list/detail pages, or asking about model architecture in front/src/model/.

Model Structure

Reference implementation: front/src/model/article

Design Philosophy

  • OOUI-based: One directory per domain model under model/
  • Feature-split: Separate list/ and detail/ for different use cases
  • Slice pattern: Each input field is an independent Zustand slice
  • Phase-based validation: Validate progressively (onChange → onDraftSubmit → onConfirmedSubmit)
  • Preview separation: Pure View components (props) vs store-connected wrappers

Key Patterns

Input Slice

Each form field has its own slice with value, setter, and validation getters.

inputs/[field]/
├── slice.ts      # Generate slice with createInputSlice factory
├── validation.ts # ArkType schema + InputValidation (when validation needed)
├── hook.ts       # useXxxInput() - select from store, compute errors
└── index.tsx     # 'use client' UI component

Slice Factory (recommended):

Use createInputSlice factory to generate slices. Reduces boilerplate.

import { createInputSlice, type InputSliceShape } from '@/model/common/lib/slice-factory'
import { titleValidation } from './validation'

// With validation
export type TitleSlice = InputSliceShape<'title', string>
export const createTitleSlice = createInputSlice('title', titleValidation)

// Without validation (enums, etc.)
export type CategorySlice = InputSliceShape<'category', ArticleCategory>
export const createCategorySlice = createInputSlice<'category', ArticleCategory>('category')

Generated slice shape: { [key], set[Key], get[Key]ErrorMessages, get[Key]IsValid }

Irregular patterns (cross-field deps, custom logic):

  • Write slice manually, or
  • Create base with factory, then spread-merge additional methods

Store Composition

Combine all slices into one store with meta functions.

  • Spread each slice: ...createXxxSlice(initialValue)(...args)
  • Add getFormIsValid() and getFormValues() to MetaSlice
  • Provide via Context + useRef pattern

Validation

  • Use constraint values from @ckda-cms/core/model/article (articleConstraints)
  • Use validateWithStandardSchema() from model/common/lib/validation.ts
  • Return InputValidation with three phases: onChange, onDraftSubmit, onConfirmedSubmit
  • Returns ValidationError[] (no isOk - errors only generated when invalid)

Pattern:

import { articleConstraints, titleSchema } from '@ckda-cms/core/model/article'
import {
  validateWithStandardSchema,
  withValidationErrorMessages,
} from '@/model/common/lib/validation'

const c = articleConstraints.title

// withValidationErrorMessagesでメッセージを追加(otherwiseはデフォルト「不正な値です」)
const titleOnSubmitSchema = withValidationErrorMessages(titleSchema, {
  minLength: 'タイトルは必須です',
  maxLength: `${c.maxLength}文字以内で入力してください`,
})

export const titleValidation = (value: string): InputValidation => ({
  onChange: validateWithStandardSchema(onChangeSchema, value),
  onConfirmedSubmit: validateWithStandardSchema(titleOnSubmitSchema, value),
  onDraftSubmit: [],
})

Query

  • Use use cache directive with cacheTag() for data fetching
  • Located in query.ts under each feature

Actions

  • Server Actions in actions.ts
  • Call API via apiClient from model/common/rpc-client.ts
  • Use revalidateTag() to invalidate cache after mutations

Preview

  • model/[entity]/preview/[field]/index.tsx: Pure View (receives value via props)
  • model/[entity]/detail/preview/[field]/index.tsx: Connects to store and renders View

Directory Structure

model/[entity]/
├── actions.ts
├── list/
│ ├── query.ts
│ ├── search/store/ + inputs/
│ └── table/
└── detail/
├── query.ts
├── store/
├── form/inputs/
└── preview/

Read front/src/model/xxx for concrete examples.

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon