本ブログは読み取り専用で、状態遷移も権限も持たない。そのため参考リポジトリのような重い集約・ドメインサービスは不要で、ここでは**「何を表すデータ(エンティティ・値)」と「テストできる純粋なルール(ドメインロジック)」**に絞って整理する。
external/層の型(microCMSのレスポンス形)と、アプリ内で使う型(Article)は分けて考える。変換はexternal/の境界で行う(規模が小さい間は同一型でも可)| エンティティ | 説明 | 主な属性 |
|---|---|---|
| Article | 記事1件。ブログの中心 | id, title, excerpt, body, categories[], card, publishedAt, eyecatch? |
| Category | 記事の分類。独立したエンティティではなく、Articleが持つカテゴリ名の文字列(microCMSのセレクトフィールドの選択肢) | name(文字列そのもの) |
| Author | 著者(サイト全体で1人)。著者カードと/profileの元になる |
name, role, bio, avatarUrl?, snsLinks[], profileImageUrl?, profileBody |
| SnsLink | 著者のSNSへの外部リンク(Authorの一部) | type(github / x など), url |
| 名前 | 使う場所 | 守るルール | 例 |
|---|---|---|---|
| ArticleCardStyle | Article.card | label(大きな装飾文字)theme の2点セット。いずれもカテゴリから導出する。themeは定義済みの値のみ |
{ label: "TypeScript", theme: "dark" } |
| CardTheme | ArticleCardStyle.theme | blue / dark / green / purple / black / yellow のいずれか |
blue |
| ArchiveMonth | Archiveの1項目 | YYYY-MM形式 |
2026-09 |
| PublishedAt | Article.publishedAt | ISO 8601の日時文字列。表示時はYYYY.MM.DDに整形 |
2026-09-14T09:00:00.000Z |
読み取り専用のため、トランザクション境界や不変条件を守る書き込み集約は存在しない。データの関係だけを示す。
[Article] ──has many──> Category(文字列) … 記事は複数カテゴリを持てる(0件も可)
[Article] ──has──> [ArticleCardStyle] … 表示用の装飾情報(VO)
[Author] ──has many──> [SnsLink] … 著者はSNSリンクを持つ
[Author] … 記事とは関連付けない(サイト全体で1人)。`/profile`の本文(HTML)も保持する
| 項目 | 内容 |
|---|---|
| 集約ルート | Article(読み取りの単位。カテゴリは参照として持つ) |
| 一意性 | ArticleId は一意。カテゴリ名は1記事内で重複しない(Reactのkeyにもカテゴリ名を使う) |
| 整合性 | カテゴリの選択肢はmicroCMS側とコード側(CATEGORIES・テーママッピング)の二重管理になる。選択肢を追加したらコード側にも反映する。マッピングにないカテゴリは既定テーマで表示する |
| ルール | 内容 | 関数(案) |
|---|---|---|
| NEWバッジ | 公開日から7日以内の記事に付ける。基準日は「現在日時」を引数で渡す(テスト可能にするため) | isNew(publishedAt, now, days = 7) |
| 公開日の表記 | YYYY.MM.DD(カード・詳細)。**日本時間(JST)**で日付を決める |
formatPublishedDate(iso) |
| 並び順 | 公開日の降順 | sortByPublishedAtDesc(articles) |
「7日」「500字/分」は初期値の提案。変更しやすいよう定数として切り出す。
| ルール | 内容 | 関数(案) |
|---|---|---|
| キーワード検索 | タイトルの部分一致。大文字小文字・全角半角の空白の違いを無視。空文字は全件 | matchesKeyword(article, q) |
| カテゴリ絞り込み | 選択したカテゴリ(カテゴリ名)を持つ記事。未選択(すべて)は全件 | matchesCategory(article, category?) |
| 月絞り込み | publishedAtのJST年月がYYYY-MMと一致 |
matchesMonth(article, month?) |
| 合成 | 上記のAND条件 | filterArticles(articles, { q, category, month }) |