ソフトウェア設計って、どうしても堅苦しく聞こえがちです。
「アーキテクチャ」と聞くだけで、急に難しそうに感じることもあります。
でも実際に触れてみると、
「もっと早く知っておけばよかった…!」
と思う概念がいくつもあります。
そのひとつが レイヤードアーキテクチャ(Layered Architecture) です。
この記事では、
- なぜレイヤーに分けるのか
- どう役に立つのか
を、できるだけ気楽に紹介していきます。
レイヤードアーキテクチャって何?
まずはざっくり。
アプリケーションを役割ごとに“層”に分けて整理する考え方
です。
料理に例えるとイメージしやすいかもしれません。
- 材料を切る人(下ごしらえ)
- 調理する人(味付け)
- 盛り付けする人(仕上げ)
それぞれが自分の役割に集中することで、料理全体のクオリティが安定します。
アプリケーションも同じで、役割を分けることで「どこに何を書くか」が明確になり、保守しやすくなります。
4つのレイヤーを図で見てみる
まずは全体像をイメージしてみましょう。
各レイヤーをもう少し丁寧に見ていく
1. Presentation(プレゼンテーション層)— ユーザーとの窓口
ユーザーからの入力を受け取り、結果を返す部分です。
Web アプリならコントローラや画面表示がここに含まれます。
- 入力を受け取る
- 結果を返す
この2つに集中します。
C#(ASP.NET Core)の例
[ApiController]
[Route("api/users")]
public class UserController : ControllerBase
{
private readonly RegisterUserUseCase _useCase;
public UserController(RegisterUserUseCase useCase)
{
_useCase = useCase;
}
[HttpPost("register")]
public IActionResult Register(RegisterUserRequest request)
{
_useCase.Handle(request.Name, request.Email);
return Ok("User registered");
}
}
public record RegisterUserRequest(string Name, string Email);
2. Application(アプリケーション層)— アプリ全体の“司令塔”
アプリが提供する機能(ユースケース)を実現するための流れを組み立てる層です。
- ビジネスルールは持たない
- 「どの処理をどの順番で呼ぶか?」に集中する
ここが太りすぎると、ビジネスルールが散らばり、テストしづらくなります。
薄い層であることが理想です。
C# の例
public class RegisterUserUseCase
{
private readonly IUserRepository _userRepository;
public RegisterUserUseCase(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public void Handle(string name, string email)
{
var user = new User(name, email);
_userRepository.Save(user);
}
}
3. Domain(ドメイン層)— アプリの“心臓部”
ビジネスルールやドメインモデルが集まる層です。
アプリケーションの寿命を左右する重要な知識がここに詰まっています。
- 外部技術に依存しない
- テストしやすい
- 長期的な保守性を高める
外部技術は数年で変わりますが、
ビジネスルールは10年以上変わらないことも多いため、
技術から切り離しておくと長期的に保守しやすくなります。
C# の例
public class User
{
public string Name { get; }
public string Email { get; }
public User(string name, string email)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Name is required");
if (!email.Contains("@"))
throw new ArgumentException("Invalid email");
Name = name;
Email = email;
}
}
4. Infrastructure(インフラ層)— 現実世界との接続
データベースや外部 API とのやり取りなど、技術的な詳細を担当する層です。
- Application/Domain からの抽象的な依頼を実装
- 技術的な詳細はここに閉じ込める
C# の例
public class UserRepository : IUserRepository
{
private readonly AppDbContext _db;
public UserRepository(AppDbContext db)
{
_db = db;
}
public void Save(User user)
{
_db.Users.Add(new UserEntity
{
Name = user.Name,
Email = user.Email
});
_db.SaveChanges();
}
}
public interface IUserRepository
{
void Save(User user);
}
レイヤーごとのクラス配置と依存関係をまとめて見てみる
ここまでの内容を、図で整理してみます。
図から読み取れるポイント
1. 依存方向は上 → 下に揃っている
Presentation → Application → Domain → Infrastructure
という流れが守られています。
2. Application は Infrastructure を知らない
IUserRepository(抽象)に依存するだけ です。
3. Infrastructure → Domain の依存は許容される
DB から User を復元するため、
UserRepository → User の依存は必要 です。
Domain は Infrastructure を知らないが、Infrastructure は Domain を知ってよい
という関係になっています。
実際の開発で意識したいこと
1. “責務”を意識する
「この処理、本当にこの層でいい?」
と自問するだけで、コードの整理が進みます。
2. 依存方向を守る
ドメイン層がインフラ層に手を伸ばし始めたら危険信号。
3. 完璧を目指しすぎない
最初から4層をガチガチに作る必要はありません。
必要になったら分ける、くらいの気持ちで十分です。
レイヤードアーキテクチャは“型”ではなく“考え方”
現場では、
- レイヤーを3つにする
- ドメイン層を薄くする
- インフラ層をモジュール化する
など、プロジェクトに合わせてアレンジされます。
大事なのは、
「なぜ分けるのか?」を理解したうえで使うこと
です。
まとめ
レイヤードアーキテクチャは、
アプリケーションを“きれいに保つ”ための強力な武器です。
- 役割ごとに層を分ける
- 依存方向を守る
- 責務を意識する
この3つを押さえておけば、
アプリはぐっと扱いやすくなります。
もし最近「コードが散らかってきたな…」と感じているなら、
まずは Application と Domain を分けるところから 始めてみるのがおすすめです。
小さな分離でも、アプリの見通しは驚くほど良くなります。