本ブログは読み取り専用で、状態遷移も権限も持たない。そのため参考リポジトリのような重い集約・ドメインサービスは不要で、ここでは**「何を表すデータ(エンティティ・値)」と「テストできる純粋なルール(ドメインロジック)」**に絞って整理する。

設計方針

エンティティ

エンティティ 説明 主な属性
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

値オブジェクト(VO)

名前 使う場所 守るルール 例
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 })