91
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

iOS開発でClean Architectureを採用した際のイイ感じのディレクトリ構成とは

Posted at

Clean Architectureを採用しているとファイル数が膨大になってしまうため、イイ感じにグルーピングして管理したいですが、レイヤ単位で分けるか画面とそれ以外で分けるか悩むと思います。(悩みました🙋)

2,3の構成を試し、その中で一番イイ感じに運用できた構成を紹介します。

その前によく見られているであろうリポジトリ※ のディレクトリ構成をパターン分けしてみました。
※「ios clean architecture」でググった際の結果とGitHub検索で出た上位結果

ディレクトリ構成のパターン

上記でさらっと挙げたように大きく2つのパターンがあると思います。
「レイヤ単位で分けているパターン」と「画面とその他で分けているパターン」です。

ここでは参考に上記の2つのパターンに分けて構成をまとめます。

以下では基本的にフォルダ名のみ記載します。
フォルダ名のみだと把握しにくいと判断したものはファイル名も記載しています。
ファイル名が記載されてないファルダの直下は同役割のファイルが全て共存していると捉えてください。

マイノリティーな命名がされているものには隣に括弧で補足しています。
(とはいえマイノリティかマジョリティかは確証がありませんのでご容赦ください。)

レイヤ単位(Data, Domain, Presentaion)で分けているパターン

├── Application
│   ├── Builder
│   └── Wireframe
├── Data
│   ├── DataStore
│   ├── Entity
│   └── Network
├── Domain
│   ├── Model
│   ├── Repository
│   └── UseCase
│		  └── Translater
├── Presentation
│   ├── Presenter
│   └── UI
│		  ├── Cell
│   	  ├── View
│   	  ├── ViewController
│   	  └── ViewModel
└── Util
├── Data
│   ├── DataType(Entity)
│   ├── Gateway(DataStore)
│   └── Repository
├── Domain
│   ├── Entity
│   ├── UseCase
│   └── ValueObject(Translator)
└── Presentation
	  ├── AAA
	  │   └── Adapter(Presenter)
	  ├── BBB
	  │   └── Adapter
	  └── ViewData(Model)
├── DataLayer
│   ├── DataStore
│   │   └── Request
│   ├── Entity
│   └── Repository
├── DomainLayer
│   ├── Model
│   ├── UseCase
│   └── ValueObject(Translator)
├── PresentationLayer
│   ├── Presenter
│   │   ├── AAA
│   │   └── BBB
│   ├── Routing
│   │   ├── AAA
│   │   └── BBB
│   ├── View
│   │   ├── AAA
│   │   └── BBB
│   └── ViewModel
│   	  ├── AAA
│   	  └── BBB
└── Utility

画面とその他で分けているパターン

├── Data 
│   ├── QiitaItem
│   │   ├── QiitaItemDataStore.swift
│   │   ├── QiitaItemEntity.swift
│   │   ├── QiitaItemRepository.swift
│   │   └── QiitaItemRequest.swift
│   └── Utility
│       ├── ApiClient.swift
│       └── ArrayTransform.swift
├── Environment 
│   ├── Const.swift
│   └── UIStoryboard+Util.swift
├── Scenes 
│   ├── List // リストページ
│   │   ├── ListConfigurator.swift
│   │   ├── Domain
│   │   │   ├── ListModel.swift
│   │   │   ├── ListTranslater.swift
│   │   │   └── ListUseCase.swift
│   │   └── Presentation
│   │       ├── Cells
│   │       │   └── ListTableViewCell.swift
│   │       ├── ListPresenter.swift
│   │       ├── ListRouter.swift
│   │       └── ListViewController.swift
│   └── Utility
│       └── Dependencies.swift
└── Storybords
    └── List.Storyboard
├── Domain  // I/F
│   ├── Entries
│   └── UseCases
├── Network // Implemented
│   ├── API
│   ├── Entries
│   ├── Network
│   └── UseCases
├── Scenes
│   └── AllPosts
│       ├── PostTableViewCell.swift
│       ├── PostsNavigator.swift(Wireframe)
│       ├── PostsViewController.swift
│       └── PostsViewModel.swift
└── Utility
├── Models
│   ├── Order.swift
│   └── ManagedOrder.swift
├── Scenes
│   ├── CreateOrder
│   │   ├── CreateOrderInteractor.swift
│   │   ├── CreateOrderModels.swift
│   │   ├── CreateOrderPresenter.swift
│   │   ├── CreateOrderRouter.swift
│   │   ├── CreateOrderViewController.swift
│   │   └── CreateOrderWorker.swift
│   └── ListOrders
│       ├── ListOrderInteractor.swift
│       ├── ListOrderModels.swift
│       ├── ListOrderPresenter.swift
│       ├── ListOrderRouter.swift
│       ├── ListOrderViewController.swift
│       └── ListOrderWorker.swift
├── Services
│   ├── OrdersAPI.swift
│   └── OrdersMemStore.swift
└── Workers
    └── OrdersWorker.swift

(個人的に)イイ感じだと思った構成

上記の3つパターンを試してみましたが RxSwift+CleanArchitectureで構成をキレイにしよう - Qiita さんの構成が一番イイ感じに運用できました。

まず、1つ目のパターンだとレイヤ毎に全てのフォルダが表示されるのでプロジェクトナビゲータが嵩張ります。
又、場合にもよりますがフォルダを開閉する際も3箇所を操作しないといけないので手間がかかりました。
表示範囲や操作回数はなるべく小さくしたいのでこのパターンは好ましくありませんでした。

逆に全てを画面単位でまとめると、横断して担うDataレイヤや各種オブジェクトが何処かに偏ってしまうので流石にこれも好ましくありませんでした。

ということで最終的にData層のみ切り分ける2の構成が一番無難に運用できています。

ちなみに自分は以下のように構成しています。(2を元に記載)

├── Application
│   └── AppDelegate.swift
├── Data 
│   └── QiitaItem
│       ├── QiitaItemDataStore.swift
│       ├── QiitaItemEntity.swift
│       ├── QiitaItemRepository.swift
│       └── QiitaItemRequest.swift
├── Scenes
│   └── List
│       ├── ListBuilder.swift
│       ├── ListWireframe.swift
│       ├── Domain
│       │   ├── ListModel.swift
│       │   ├── ListTranslater.swift
│       │   └── ListUseCase.swift
│       └── Presentation
│           ├── ListPresenter.swift
│           └── View
│               ├── Cell
│               │   ├── ListTableViewCell.swift
│               │   └── ListTableViewCell.xib
│               ├── ListViewController.swift
│               └── ListViewController.storyboard
└── Utility

最後に

Clean Architectureと括っても、+αでBuilderパターンやWireframe、Routing、一部にMVVMを採用していたり、プロジェクト(≒人)によって色々な実装方法や細分化があるので結局これといったベストプラクティスはないと思いました。

例えば、 まだiOSアプリのArchitecture選定で消耗してるの? - Qiita一見複雑そうな機能を持つアプリでも、1画面1APIのようなI/F設計をしている場合は、サーバ側がロジック部を担保することになるのでClean ArchitectureのようなModel層の細分化はかなり冗長になると思います の通り、1画面1API程度で採用した場合は画面毎に全てまとめたほうが余計なフォルダを開かなくて済みます。

Clean Architectureの構成に限らず、ディレクトリ構成による手間を減らすテクニックなどがあれば共有していただけると幸いです。

最後の最後に

プロジェクト内検索でファイルを開いたほうが早いことも多いのでXcodeのショートカットを覚えましょう。
ショートカットcommand + shift + o

ちなみに開いた後にcommand + shift + j を押すとプロジェクトナビゲータが移動します。

91
62
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
91
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?