[Swift版]クリーンアーキテクチャ実践編②（Data層）

# はじめに
最近巷で、熱い『クリーンアーキテクチャ』の第二弾です。
今回はData層を中心に纏めました。

## やること

* UI層
 - View
 - ViewController
 - Presenter
 
* Domain層
 - Usecase *← ここ説明します*
 - Repository *← ここ説明します*
 - Entity
 
* Data層
 - Repository *← ここ説明します*
 - Entity
 - DataStore *← ここ説明します*

## やらないこと
* クリーンアーキテクチャーの概念について
* UI層について
* Domain層について →　　[[Swift版]クリーンアーキテクチャ実践編①（Domain層）](http://qiita.com/eKushida/items/f2ff5453734104ab29a9)を参照してください。

# ご説明する例
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だと思います。

1. Domain層からデータにアクセスするためには、Data層のRepositoryを通してアクセスする。
2. Domain層は、保存先がサーバー(API経由）なのかアプリ内(CoreData、UserDefaultsなど）か意識しない。

*誤りやもっとこうしたほうが良いとというアドバイスがございましたら、ご指摘頂けますと幸いです。*

こちらも合わせて読んで頂ければ幸いです。
[[Swift版]クリーンアーキテクチャ実践編①（Domain層）](http://qiita.com/eKushida/items/f2ff5453734104ab29a9)