1. ユースケース
1.1. 認証
1.2. ユーザー管理
1.3. マスタ管理
1.3.1. 部門
1.3.2. 社員
1.3.3. 商品
1.4. 監査
1.5. ダウンロード
2. 仕様
2.1. 認証
# language: ja
機能: ユーザー認証
管理者として
アプリケーションユーザーの認証をしたい
なぜなら認証されたユーザーしか利用できないから
背景:
前提:UC001 ユーザーが登録されている
シナリオ: 管理者を認証する
もし:UC001 "管理者権限" でログインする
ならば:UC001 "管理者" として認証される
シナリオ: ユーザーを認証する
もし:UC001 "利用者権限" でログインする
ならば:UC001 "利用者" として認証される
2.2. ユーザー管理
# language: ja
機能: ユーザー管理
管理者として
アプリケーションユーザーの登録をしたい
なぜなら登録されたユーザーしか利用できないから
背景:
前提:UC001 ユーザーが登録されている
前提:UC002 "管理者" である
シナリオ: ユーザー一覧を取得する
もし:UC002 "ユーザー一覧" を取得する
ならば:UC002 "ユーザー一覧" を取得できる
シナリオ: ユーザーを新規登録する
もし:UC002 ユーザーID "U000005" パスワード "a234567Z" で新規登録する
ならば:UC002 "ユーザーを登録しました" が表示される
もし:UC002 ユーザーID "U000005" パスワード "a234567Z" で認証する
ならば:UC002 ユーザーが認証される
シナリオ: ユーザーを取得する
もし:UC002 ユーザーID "U000005" パスワード "a234567Z" で新規登録する
もし:UC002 ユーザーID "U000005" で検索する
ならば:UC002 ユーザー "U000005" を取得できる
シナリオ: 登録済みユーザーを更新登録する
もし:UC002 ユーザーID "U000005" パスワード "a234567Z" で新規登録する
かつ:UC002 ユーザーID "U000005" の情報を更新する
ならば:UC002 "ユーザーを更新しました" が表示される
もし:UC002 ユーザーID "U000005" パスワード "a234567X" で認証する
ならば:UC002 ユーザーが認証される
シナリオ: 登録済みユーザーを削除する
もし:UC002 ユーザーID "U000005" パスワード "a234567Z" で新規登録する
かつ:UC002 ユーザーID "U000005" を削除する
ならば:UC002 "ユーザーを削除しました" が表示される
2.3. マスタ管理
2.3.1. 部門
# language: ja
機能: 部門マスタ管理
管理者として
部門の管理をしたい
なぜならマスタデータが必要だから
背景:
前提:UC001 ユーザーが登録されている
前提:UC003 "管理者" である
前提:UC003 "部門データ" が登録されている
シナリオ: 部門一覧を取得する
もし:UC003 "部門一覧" を取得する
ならば:UC003 "部門一覧" を取得できる
シナリオ: 部門を新規登録する
もし:UC003 部門コード "90000" 部門名 "営業部" で新規登録する
ならば:UC003 "部門を登録しました" が表示される
シナリオ: 部門を取得する
もし:UC003 部門コード "90000" 部門名 "営業部" で新規登録する
もし:UC003 部門コード "90000" で検索する
ならば:UC003 "営業部" の部門が取得できる
シナリオ: 登録済み部門を更新登録する
もし:UC003 部門コード "90000" 部門名 "営業部" で新規登録する
かつ:UC003 部門コード "90000" の情報を更新する (部門名 "マーケティング部")
ならば:UC003 "部門を更新しました" が表示される
もし:UC003 部門コード "90000" で検索する
ならば:UC003 "マーケティング部" の部門が取得できる
シナリオ: 登録済み部門を削除する
もし:UC003 部門コード "90000" 部門名 "営業部" で新規登録する
かつ:UC003 部門コード "90000" を削除する
ならば:UC003 "部門を削除しました" が表示される
シナリオ: 部門を検索する
もし:UC003 開始期間を "2021-01-01T00:00:00+09:00" から "9999-12-31T00:00:00+09:00" で検索する
ならば:UC003 検索結果一覧を取得できる
2.3.2. 社員
# language: ja
機能: 社員マスタ管理
管理者として
社員の管理をしたい
なぜならマスタデータが必要だから
背景:
前提:UC001 ユーザーが登録されている
前提:UC004 "管理者" である
前提:UC004 "部門データ" が登録されている
前提:UC004 "社員データ" が登録されている
シナリオ: 社員一覧を取得する
もし:UC004 "社員一覧" を取得する
ならば:UC004 "社員一覧" を取得できる
シナリオ: 社員を新規登録する
もし:UC004 社員コード "EMP999" 社員名 "山田 太郎" 社員名カナ "ヤマダ タロウ" で新規登録する
ならば:UC004 "社員を登録しました" が表示される
シナリオ: 社員を取得する
もし:UC004 社員コード "EMP999" 社員名 "山田 太郎" 社員名カナ "ヤマダ タロウ" で新規登録する
もし:UC004 社員コード "EMP999" で検索する
ならば:UC004 社員 "山田 太郎" を取得できる
シナリオ: 登録済み社員を更新登録する
もし:UC004 社員コード "EMP999" 社員名 "山田 太郎" 社員名カナ "ヤマダ タロウ" で新規登録する
かつ:UC004 社員コード "EMP999" の情報を更新する (社員名 "山田 花子" 社員名カナ "ヤマダ ハナコ")
ならば:UC004 "社員を更新しました" が表示される
もし:UC004 社員コード "EMP999" で検索する
ならば:UC004 社員 "山田 花子" を取得できる
シナリオ: 登録済み社員を削除する
もし:UC004 社員コード "EMP999" 社員名 "山田 太郎" 社員名カナ "ヤマダ タロウ" で新規登録する
かつ:UC004 社員コード "EMP999" を削除する
ならば:UC004 "社員を削除しました" が表示される
シナリオ: 社員を検索する
もし:UC004 社員コード "EMP999" 社員名 "山田 太郎" 社員名カナ "ヤマダ タロウ" で新規登録する
もし:UC004 社員コード "EMP998" 社員名 "山田 ジロウ" 社員名カナ "ヤマダ ジロウ" で新規登録する
もし:UC004 社員名 "山田" で検索する
ならば:UC004 検索結果一覧を取得できる
2.3.3. 商品
# language: ja
機能: 商品マスタ管理
管理者として
商品の管理をしたい
なぜならマスタデータが必要だから
背景:
前提:UC001 ユーザーが登録されている
前提:UC005 "管理者" である
前提:UC005 "商品データ" が登録されている
シナリオ: 商品一覧を取得する
もし:UC005 "商品一覧" を取得する
ならば:UC005 "商品一覧" を取得できる
シナリオ: 商品を新規登録する
もし:UC005 商品コード "10101001" 商品名 "鶏ささみ" で新規登録する
ならば:UC005 "商品を登録しました" が表示される
シナリオ: 商品を取得する
もし:UC005 商品コード "10101001" 商品名 "鶏ささみ" で新規登録する
もし:UC005 商品コード "10101001" で検索する
ならば:UC005 "鶏ささみ" の商品が取得できる
シナリオ: 登録済み商品を更新登録する
もし:UC005 商品コード "10101001" 商品名 "鶏ささみ" で新規登録する
かつ:UC005 商品コード "10101001" の情報を更新する (商品名 "鶏レバー")
ならば:UC005 "商品を更新しました" が表示される
もし:UC005 商品コード "10101001" で検索する
ならば:UC005 "鶏レバー" の商品が取得できる
シナリオ: 登録済み商品を削除する
もし:UC005 商品コード "10101001" 商品名 "鶏ささみ" で新規登録する
かつ:UC005 商品コード "10101001" を削除する
ならば:UC005 "商品を削除しました" が表示される
シナリオ: 商品を検索する
もし:UC005 商品区分 "その他" で検索する
ならば:UC005 商品検索結果一覧を取得できる
シナリオ: 商品分類一覧を取得する
もし:UC005 "商品分類一覧" を取得する
ならば:UC005 "商品分類一覧" を取得できる
シナリオ: 商品分類を新規登録する
もし:UC005 商品分類コード "00101000" 商品分類名 "食肉" で新規登録する
ならば:UC005 "商品分類を登録しました" が表示される
シナリオ: 商品分類を取得する
もし:UC005 商品分類コード "00101000" 商品分類名 "食肉" で新規登録する
もし:UC005 商品分離コード "00101000" で検索する
ならば:UC005 "食肉" の商品分類が取得できる
シナリオ: 登録済み商品分類を更新登録する
もし:UC005 商品分類コード "00101000" 商品分類名 "食肉" で新規登録する
かつ:UC005 商品分類コード "00101000" の情報を更新する (商品分類名 "鶏肉")
ならば:UC005 "商品分類を更新しました" が表示される
もし:UC005 商品分離コード "00101000" で検索する
ならば:UC005 "鶏肉" の商品分類が取得できる
シナリオ: 登録済み商品分類を削除する
もし:UC005 商品分類コード "00101000" 商品分類名 "食肉" で新規登録する
かつ:UC005 商品分類コード "00101000" を削除する
ならば:UC005 "商品分類を削除しました" が表示される
シナリオ: 商品分類を検索する
もし:UC005 商品分類パス "カテゴリ1" で検索する
ならば:UC005 商品分類検索結果一覧を取得できる
2.4. 監査
# language: ja
機能: 監査
管理者として
アプリケーションの実行履歴を把握したい
なぜならユーザーの操作履歴を監査する必要があるから
背景:
前提:UC001 ユーザーが登録されている
前提:UC006 "管理者" である
前提:UC006 アプリケーションが実行されている
シナリオ: 実行履歴一覧を取得する
もし:UC006 "アプリケーション実行履歴" を取得する
ならば:UC006 "アプリケーション実行履歴一覧" を取得できる
シナリオ: 実行履歴を取得する
もし:UC006 アプリケーション実行履歴を検索する
ならば:UC006 アプリケーション実行履歴を取得できる
シナリオ: 実行履歴を削除する
もし:UC006 アプリケーション実行履歴を削除する
ならば:UC006 "アプリケーション実行履歴を削除しました" が表示される
2.5. ダウンロード
# language: ja
機能: ダウンロード
管理者として
アプリケーションのダウンロードを実行したい
なぜならデータを加工するために必要だから
背景:
前提:UC001 ユーザーが登録されている
前提:UC007 "管理者" である
前提:UC007 ダウンロードデータが存在する
シナリオ: ダウンロード件数を取得する
もし:UC007 "部門" ダウンロード件数を取得する
ならば:UC007 ダウンロード件数を取得できる
シナリオ: ダウンロードを実行する
もし:UC007 "部門" ダウンロードを実行する
ならば:UC007 ダウンロードデータが作成される
3. アーキテクチャ
3.1. フロントエンド
3.2. バックエンド
package com.example.sms;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@AnalyzeClasses(packages = "com.example.sms")
@DisplayName("アーキテクチャルルール")
public class ArchitectureRuleTest {
@Test
@DisplayName("プレゼンテーション層はサービス層とドメイン層にアクセスできる")
public void presentationLayerShouldOnlyAccessServiceLayerAndDomainLayer() {
JavaClasses importedClasses = new ClassFileImporter().importPackages("com.example.sms");
ArchRuleDefinition.noClasses()
.that()
.resideInAPackage("..presentation..")
.should()
.accessClassesThat()
.resideInAPackage("..infrastructure..")
.check(importedClasses);
}
@Test
@DisplayName("サービス層はドメイン層とインフラストラクチャ層のみにアクセスできる")
public void serviceLayerShouldOnlyAccessDomainAndInfrastructureLayers() {
JavaClasses importedClasses = new ClassFileImporter().importPackages("com.example.sms");
ArchRuleDefinition.noClasses()
.that()
.resideInAPackage("..service..")
.should()
.accessClassesThat()
.resideInAPackage("..presentation..")
.check(importedClasses);
}
@Test
@DisplayName("ドメイン層は他の層にアクセスできない")
public void domainLayerShouldNotAccessOtherLayers() {
JavaClasses importedClasses = new ClassFileImporter().importPackages("com.example.sms");
ArchRuleDefinition.noClasses()
.that()
.resideInAPackage("..domain..")
.should()
.accessClassesThat()
.resideInAPackage("..presentation..")
.check(importedClasses);
ArchRuleDefinition.noClasses()
.that()
.resideInAPackage("..domain..")
.should()
.accessClassesThat()
.resideInAPackage("..service..")
.check(importedClasses);
ArchRuleDefinition.noClasses()
.that()
.resideInAPackage("..domain..")
.should()
.accessClassesThat()
.resideInAPackage("..infrastructure..")
.check(importedClasses);
}
@Test
@DisplayName("インフラストラクチャ層はドメイン層以外にアクセスできない")
public void infrastructureLayerShouldNotAccessNonDomainLayers() {
JavaClasses importedClasses = new ClassFileImporter().importPackages("com.example.sms");
ArchRuleDefinition.noClasses()
.that()
.resideInAPackage("..infrastructure..")
.should()
.accessClassesThat()
.resideInAPackage("..presentation..")
.check(importedClasses);
ArchRuleDefinition.noClasses()
.that()
.resideInAPackage("..infrastructure..")
.should()
.accessClassesThat()
.resideInAPackage("..service..")
.check(importedClasses);
}
}