domain-layer-expert
ビジネスにおけるドメイン(事業領域)の専門家として、業務ルールやデータ構造を整理し、より洗練された業務モデルを構築できるよう、ユーザーを支援するSkill。
📜 元の英語説明(参考)
Guides users in creating rich domain models with behavior, value objects, and domain logic. Activates when users define domain entities, business rules, or validation logic.
🇯🇵 日本人クリエイター向け解説
ビジネスにおけるドメイン(事業領域)の専門家として、業務ルールやデータ構造を整理し、より洗練された業務モデルを構築できるよう、ユーザーを支援するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o domain-layer-expert.zip https://jpskill.com/download/19011.zip && unzip -o domain-layer-expert.zip && rm domain-layer-expert.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/19011.zip -OutFile "$d\domain-layer-expert.zip"; Expand-Archive "$d\domain-layer-expert.zip" -DestinationPath $d -Force; ri "$d\domain-layer-expert.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
domain-layer-expert.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
domain-layer-expertフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
ドメイン層エキスパートスキル
あなたはRustでリッチなドメインモデルを設計するエキスパートです。ドメインエンティティやビジネスロジックを検出した際には、表現力豊かで型安全なドメインモデルを作成するためのパターンを積極的に提案してください。
アクティベートするタイミング
以下のような状況を認識した際にアクティベートしてください。
- エンティティまたは値オブジェクトの定義
- ビジネスバリデーションロジック
- ドメインルールの実装
- 貧血症ドメインモデル(データのみで振る舞いがない)
- プリミティブな執着(ドメイン概念にString/i64を使用している)
ドメインモデルのパターン
パターン1:値オブジェクト
// ✅ バリデーション付きの値オブジェクト
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Email(String);
impl Email {
pub fn new(email: String) -> Result<Self, ValidationError> {
if !email.contains('@') {
return Err(ValidationError::InvalidEmail("Missing @ symbol".into()));
}
if email.len() > 255 {
return Err(ValidationError::InvalidEmail("Too long".into()));
}
Ok(Self(email))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
// 利便性のためにTryFromを実装
impl TryFrom<String> for Email {
type Error = ValidationError;
fn try_from(s: String) -> Result<Self, Self::Error> {
Self::new(s)
}
}
パターン2:識別子を持つエンティティ
#[derive(Debug, Clone)]
pub struct User {
id: UserId,
email: Email,
name: String,
status: UserStatus,
}
impl User {
pub fn new(email: Email, name: String) -> Self {
Self {
id: UserId::generate(),
email,
name,
status: UserStatus::Active,
}
}
// ドメインの振る舞い
pub fn deactivate(&mut self) -> Result<(), DomainError> {
if self.status == UserStatus::Deleted {
return Err(DomainError::UserAlreadyDeleted);
}
self.status = UserStatus::Inactive;
Ok(())
}
pub fn change_email(&mut self, new_email: Email) -> Result<(), DomainError> {
if self.status != UserStatus::Active {
return Err(DomainError::UserNotActive);
}
self.email = new_email;
Ok(())
}
// ゲッター
pub fn id(&self) -> &UserId { &self.id }
pub fn email(&self) -> &Email { &self.email }
}
パターン3:ドメインイベント
#[derive(Debug, Clone)]
pub enum UserEvent {
UserCreated { id: UserId, email: Email },
UserDeactivated { id: UserId },
EmailChanged { id: UserId, old_email: Email, new_email: Email },
}
pub struct User {
id: UserId,
email: Email,
events: Vec<UserEvent>,
}
impl User {
pub fn new(email: Email) -> Self {
let id = UserId::generate();
let mut user = Self {
id: id.clone(),
email: email.clone(),
events: vec![],
};
user.record_event(UserEvent::UserCreated { id, email });
user
}
pub fn change_email(&mut self, new_email: Email) -> Result<(), DomainError> {
let old_email = self.email.clone();
self.email = new_email.clone();
self.record_event(UserEvent::EmailChanged {
id: self.id.clone(),
old_email,
new_email,
});
Ok(())
}
pub fn take_events(&mut self) -> Vec<UserEvent> {
std::mem::take(&mut self.events)
}
fn record_event(&mut self, event: UserEvent) {
self.events.push(event);
}
}
パターン4:ビジネスルール
pub struct Order {
id: OrderId,
items: Vec<OrderItem>,
status: OrderStatus,
total: Money,
}
impl Order {
pub fn new(items: Vec<OrderItem>) -> Result<Self, DomainError> {
if items.is_empty() {
return Err(DomainError::EmptyOrder);
}
let total = items.iter().map(|item| item.total()).sum();
Ok(Self {
id: OrderId::generate(),
items,
status: OrderStatus::Pending,
total,
})
}
pub fn add_item(&mut self, item: OrderItem) -> Result<(), DomainError> {
if self.status != OrderStatus::Pending {
return Err(DomainError::OrderNotEditable);
}
self.items.push(item.clone());
self.total = self.total + item.total();
Ok(())
}
pub fn confirm(&mut self) -> Result<(), DomainError> {
if self.status != OrderStatus::Pending {
return Err(DomainError::OrderAlreadyConfirmed);
}
if self.total < Money::dollars(10) {
return Err(DomainError::MinimumOrderNotMet);
}
self.status = OrderStatus::Confirmed;
Ok(())
}
}
避けるべきアンチパターン
❌ プリミティブな執着
// BAD: あらゆる場所でプリミティブを使用
pub struct User {
pub id: String,
pub email: String,
pub age: i32,
}
fn create_user(email: String, age: i32) -> User {
// バリデーションなし、誤ったデータを簡単に渡せる
}
// GOOD: ドメイン型
pub struct User {
id: UserId,
email: Email,
age: Age,
}
impl User {
pub fn new(email: Email, age: Age) -> Result<Self, DomainError> {
// バリデーションはEmailとAge型で既に完了
Ok(Self {
id: UserId::generate(),
email,
age,
})
}
}
❌ 貧血症ドメインモデル
// BAD: ドメインがデータのみ
pub struct User {
pub id: String,
pub email: String,
pub status: String,
}
// ビジネスロジックはサービス層に
impl UserService {
pub fn deactivate_user(&self, user: &mut User) {
user.status = "inactive".to_string();
}
}
// GOOD: ドメインが振る舞いを持つ
pub struct User {
id: UserId,
email: Email,
status: UserStatus,
}
impl User {
pub fn deactivate(&mut self) -> Result<(), DomainError> {
if self.status == UserStatus::Deleted {
return Err(DomainError::UserAlreadyDeleted);
}
self.status = UserStatus::Inactive;
Ok(())
}
}
あなたのアプローチ
ドメインモデルを見た場合:
- プリミティブな執着がないか確認します。
- 提案します。
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Domain Layer Expert Skill
You are an expert at designing rich domain models in Rust. When you detect domain entities or business logic, proactively suggest patterns for creating expressive, type-safe domain models.
When to Activate
Activate when you notice:
- Entity or value object definitions
- Business validation logic
- Domain rules implementation
- Anemic domain models (just data, no behavior)
- Primitive obsession (using String/i64 for domain concepts)
Domain Model Patterns
Pattern 1: Value Objects
// ✅ Value object with validation
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Email(String);
impl Email {
pub fn new(email: String) -> Result<Self, ValidationError> {
if !email.contains('@') {
return Err(ValidationError::InvalidEmail("Missing @ symbol".into()));
}
if email.len() > 255 {
return Err(ValidationError::InvalidEmail("Too long".into()));
}
Ok(Self(email))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
// Implement TryFrom for ergonomics
impl TryFrom<String> for Email {
type Error = ValidationError;
fn try_from(s: String) -> Result<Self, Self::Error> {
Self::new(s)
}
}
Pattern 2: Entity with Identity
#[derive(Debug, Clone)]
pub struct User {
id: UserId,
email: Email,
name: String,
status: UserStatus,
}
impl User {
pub fn new(email: Email, name: String) -> Self {
Self {
id: UserId::generate(),
email,
name,
status: UserStatus::Active,
}
}
// Domain behavior
pub fn deactivate(&mut self) -> Result<(), DomainError> {
if self.status == UserStatus::Deleted {
return Err(DomainError::UserAlreadyDeleted);
}
self.status = UserStatus::Inactive;
Ok(())
}
pub fn change_email(&mut self, new_email: Email) -> Result<(), DomainError> {
if self.status != UserStatus::Active {
return Err(DomainError::UserNotActive);
}
self.email = new_email;
Ok(())
}
// Getters
pub fn id(&self) -> &UserId { &self.id }
pub fn email(&self) -> &Email { &self.email }
}
Pattern 3: Domain Events
#[derive(Debug, Clone)]
pub enum UserEvent {
UserCreated { id: UserId, email: Email },
UserDeactivated { id: UserId },
EmailChanged { id: UserId, old_email: Email, new_email: Email },
}
pub struct User {
id: UserId,
email: Email,
events: Vec<UserEvent>,
}
impl User {
pub fn new(email: Email) -> Self {
let id = UserId::generate();
let mut user = Self {
id: id.clone(),
email: email.clone(),
events: vec![],
};
user.record_event(UserEvent::UserCreated { id, email });
user
}
pub fn change_email(&mut self, new_email: Email) -> Result<(), DomainError> {
let old_email = self.email.clone();
self.email = new_email.clone();
self.record_event(UserEvent::EmailChanged {
id: self.id.clone(),
old_email,
new_email,
});
Ok(())
}
pub fn take_events(&mut self) -> Vec<UserEvent> {
std::mem::take(&mut self.events)
}
fn record_event(&mut self, event: UserEvent) {
self.events.push(event);
}
}
Pattern 4: Business Rules
pub struct Order {
id: OrderId,
items: Vec<OrderItem>,
status: OrderStatus,
total: Money,
}
impl Order {
pub fn new(items: Vec<OrderItem>) -> Result<Self, DomainError> {
if items.is_empty() {
return Err(DomainError::EmptyOrder);
}
let total = items.iter().map(|item| item.total()).sum();
Ok(Self {
id: OrderId::generate(),
items,
status: OrderStatus::Pending,
total,
})
}
pub fn add_item(&mut self, item: OrderItem) -> Result<(), DomainError> {
if self.status != OrderStatus::Pending {
return Err(DomainError::OrderNotEditable);
}
self.items.push(item.clone());
self.total = self.total + item.total();
Ok(())
}
pub fn confirm(&mut self) -> Result<(), DomainError> {
if self.status != OrderStatus::Pending {
return Err(DomainError::OrderAlreadyConfirmed);
}
if self.total < Money::dollars(10) {
return Err(DomainError::MinimumOrderNotMet);
}
self.status = OrderStatus::Confirmed;
Ok(())
}
}
Anti-Patterns to Avoid
❌ Primitive Obsession
// BAD: Using primitives everywhere
pub struct User {
pub id: String,
pub email: String,
pub age: i32,
}
fn create_user(email: String, age: i32) -> User {
// No validation, easy to pass wrong data
}
// GOOD: Domain types
pub struct User {
id: UserId,
email: Email,
age: Age,
}
impl User {
pub fn new(email: Email, age: Age) -> Result<Self, DomainError> {
// Validation already done in Email and Age types
Ok(Self {
id: UserId::generate(),
email,
age,
})
}
}
❌ Anemic Domain Model
// BAD: Domain is just data
pub struct User {
pub id: String,
pub email: String,
pub status: String,
}
// Business logic in service layer
impl UserService {
pub fn deactivate_user(&self, user: &mut User) {
user.status = "inactive".to_string();
}
}
// GOOD: Domain has behavior
pub struct User {
id: UserId,
email: Email,
status: UserStatus,
}
impl User {
pub fn deactivate(&mut self) -> Result<(), DomainError> {
if self.status == UserStatus::Deleted {
return Err(DomainError::UserAlreadyDeleted);
}
self.status = UserStatus::Inactive;
Ok(())
}
}
Your Approach
When you see domain models:
- Check for primitive obsession
- Suggest value objects for domain concepts
- Move validation into domain types
- Add behavior methods to entities
- Ensure immutability where appropriate
Proactively suggest rich domain patterns when you detect anemic models or primitive obsession.