LoginSignup
64
60

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-07-25

はじめに

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

やること

  • UI層

    • View
    • ViewController
    • Presenter
  • Domain層

    • Usecase ← ここ説明します
    • Repository ← ここ説明します
    • Entity
  • Data層

    • Repository ← ここ説明します
    • Entity
    • DataStore ← ここ説明します

やらないこと

ご説明する例

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層)

64
60
1

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
64
60