はじめに
最近巷で、熱い『クリーンアーキテクチャ』の第二弾です。
今回はData層を中心に纏めました。
やること
- UI層
- View
- ViewController
- Presenter
- Domain層
- Usecase ← ここ説明します
- Repository ← ここ説明します
- Entity
- Data層
- Repository ← ここ説明します
- Entity
- DataStore ← ここ説明します
やらないこと
- クリーンアーキテクチャーの概念について
- UI層について
- Domain層について → [Swift版]クリーンアーキテクチャ実践編①(Domain層)を参照してください。
ご説明する例
Data層のRepositoryからユーザ情報にアクセスする際の事例
レイヤ | カテゴリ | クラス名 | 説明 |
---|---|---|---|
Domain層 | Repository | UserRepository | ユーザ情報とのインターフェース |
Usecase | UserUseCase | 業務ロジック | |
Data層 | Repository | UserRepositoryImpl | ユーザ情報へアクセスする実体のクラス |
DataStore | UserDataStore | 永続的なデータへアクセスするクラス | |
UserDataStoreInStorage | UserDefaultsへアクセスするクラス |
1. Domain層のRepositoryについて
UserRepository
import Foundation
protocol UserRepository: class {
func save(user: User)
func user() -> User
func delete()
}
2. Data層のDataStore(UserDefaults)について
UserDefaultsへユーザ情報を保存または、参照するクラス
UserDataStoreInStorage.swift
import Foundation
enum UserDataStoreInStorage: String {
case birthDay
case mailAddress
func save(value: AnyObject) {
NSUserDefaults.standardUserDefaults().setObject(value, forKey: self.rawValue)
NSUserDefaults.standardUserDefaults().synchronize()
}
func find() -> AnyObject? {
return NSUserDefaults.standardUserDefaults().objectForKey(self.rawValue)
}
}
3. Data層のDataStoreについて
永続的なデータへアクセスするクラス
今回保存先は、UserDefaultsとしました。
ここは適宜、plistやDB、APIに置き換えてください。
UserDataStore.swift
import Foundation
class UserDataStore: NSObject {
class func user() -> User {
let user = User()
user.mailAddress = UserDataStoreInStorage.mailAddress.find() as? String ?? ""
user.birthDay = UserDataStoreInStorage.birthDay.find() as? String ?? ""
return user
}
class func save(user: User) {
UserDataStoreInStorage.mailAddress.save(user.mailAddress)
UserDataStoreInStorage.birthDay.save(user.birthDay)
}
class func delete() {
UserDataStoreInStorage.mailAddress.save("")
UserDataStoreInStorage.birthDay.save("")
}
}
4. Data層のRepositoryについて
Data層のRepositoryは、データの保存先は意識しない。
UserRepositoryImpl.swift
class UserRepositoryImpl: UserRepository {
func save(user: User) {
UserDataStore.save(user)
}
func user() -> User {
return UserDataStore.user()
}
func delete() {
UserDataStore.delete()
}
}
5. Domain層のUsecaseについて
UserUseCase.swift
import Foundation
class UserUseCase: NSObject {
let repository: UserRepository
init(repository: UserRepository) {
self.repository = repository
super.init()
}
//メールアドレスを保存する
func saveMailAddress(mailAddress: String) {
let user = repository.user()
user.mailAddress = mailAddress
repository.save(user)
}
//メールアドレスを取得する
func mailAddress() -> String {
return repository.user().mailAddress
}
//誕生日を保存する
func saveBirthDay(birthDaty: String) {
let user = repository.user()
user.birthDay = birthDaty
repository.save(user)
}
//誕生日を取得する
func birthDay() -> String {
return repository.user().birthDay
}
}
[おまけ] ユニットテストを書こう!
Data層をユニットテストする
UserDataStoreTest.swift
import XCTest
class UserDataStoreTest: XCTestCase {
let repository = UserRepositoryImpl()
var user = User()
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
}
func testStoreAndFind() {
user.mailAddress = "vision@smartdt.jp"
user.birthDay = "2011-08-01"
repository.save(user)
user = repository.user()
XCTAssertEqual("vision@smartdt.jp", user.mailAddress)
XCTAssertEqual("2011-08-01", user.birthDay)
}
func testDeleteAndFind() {
repository.delete()
user = repository.user()
XCTAssertEqual("", user.mailAddress)
XCTAssertEqual("", user.birthDay)
}
}
Domain層をユニットテストする
UserUsecaseTest.swift
import XCTest
class UserUsecaseTest: XCTestCase {
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
}
func testStoreAndFind() {
let repository = UserRepositoryImpl()
let usecase = UserUseCase(repository: repository)
usecase.saveMailAddress("vision@smartdt.jp")
usecase.saveBirthDay("2011-08-01")
XCTAssertEqual("vision@smartdt.jp", usecase.mailAddress())
XCTAssertEqual("2011-08-01", usecase.birthDay())
}
}
まとめ
ポイントは、Data層のRepositoryだと思います。
- Domain層からデータにアクセスするためには、Data層のRepositoryを通してアクセスする。
- Domain層は、保存先がサーバー(API経由)なのかアプリ内(CoreData、UserDefaultsなど)か意識しない。
誤りやもっとこうしたほうが良いとというアドバイスがございましたら、ご指摘頂けますと幸いです。
こちらも合わせて読んで頂ければ幸いです。
[Swift版]クリーンアーキテクチャ実践編①(Domain層)