Phân tích Clean Architecture - Part 3: Application

1. Mối liên hệ giữa Application-Domain:

Ở phần Application này, ta sẽ thấy rõ được mối liên kết giữa bộ ba Application - Platform - Domain. Thực sự thì application là nơi chứa phần interface (hay cụ thể hơn là nơi chứa View Controller, View Model hay là Storyboard), tóm lại là nơi các bạn hay code như bình thường ấy. 

Ở đây, cấu trúc chính là MVVM và kết hợp RxSwift như là 1 framework tối ưu. Mình sẽ không đi sâu phân tích vấn đề này. Mình chỉ có 1 take note hay cho các bạn ở đây như sau:

Chỉ là style code thôi nhưng mình rất thích, nó cực kì rõ ràng khi dùng MVVM. Ta có thể thấy rõ được tất cả các input tập trung 1 nơi và các output sẽ bắt đầu là output.signalA v..v. rất dễ theo dõi code. Ví dụ như:

Các bạn có thể thấy rất rõ ràng như trên, các tin hiệu Input, Output ở những phần riêng biệt rạch ròi cho nên rất dễ follows. Như vậy đã xong phần take note. Quay lại phần chính: 

Có 1 dòng code để lấy tín hiệu ra ở PostsViewModel, đó là:  self.useCase.posts(). Các bạn tập trung vào những đoạn code mình lấy riêng ra nha:
1. Tại ViewModel, nơi handle code, get data từ API ta có:

 private let useCase: PostsUseCase

 init(useCase: PostsUseCase, navigator: PostsNavigator) {
        self.useCase = useCase
        self.navigator = navigator
   } 

 self.useCase.posts()

2. Tại RealmPlatform, postUseCase platform. Mình sẽ không đi sâu repository làm gì. Mình khuyên các bạn nên tạm hiểu đây là 1 repo với func queryAll() return được 1 Observable<[Post]>. Sau khi các bạn hiểu hết flow, các bạn chuyển sang bài viết phân tích cụ thể repository của mình rồi đọc lại bài này. Sẽ ok hơn.

Như vậy tại đây, RealmPlatform đã implementation cái mà Domain yêu cầu.

func posts() {
   return repository.queryAll()
}

Nhưng, vấn đề là useCase đang gọi đến 1 protocol, đâu thấy dòng code nào chỉ đến đoạn RealmPlatform đâu? Yeah chính xác luôn. Phần tiếp theo sẽ đi phân tích đoạn liên hệ đấy.

2. Mối liên hệ giữa Platform-Application:

Code bên trong AppDelegate như trên, rõ ràng ta sẽ quan tâm đến code tại class Application. Mình xin trích code của class Application những phần quan trọng: 

final class Application {
    static let shared = Application()
    private let realmUseCaseProvider: Domain.UseCaseProvider

    private init() {
        self.realmUseCaseProvider = RealmPlatform.UseCaseProvider()
    }

    func configureMainInterface(in window: UIWindow) {
        let rmNavigationController = UINavigationController()
        rmNavigationController.tabBarItem = UITabBarItem(title: "Realm",
                image: UIImage(named: "Toolbox"),
                selectedImage: nil)
        let rmNavigator = DefaultPostsNavigator(services: realmUseCaseProvider,
                navigationController: rmNavigationController,
                storyBoard: storyboard)

        let tabBarController = UITabBarController()
        tabBarController.viewControllers = [
                cdNavigationController,
                rmNavigationController,
                networkNavigationController
        ]
        window.rootViewController = tabBarController

        cdNavigator.toPosts()
        rmNavigator.toPosts()
    }
}

Và ta sẽ đi sâu vào DefaultPostsNavigator trước khi phân tích toàn cục, và mình cũng sẽ chỉ lấy ra những take note quan trọng:

protocol PostsNavigator {
    func toCreatePost()
    func toPost(_ post: Post)
    func toPosts()
}


class DefaultPostsNavigator: PostsNavigator {
    private let storyBoard: UIStoryboard
    private let navigationController: UINavigationController
    private let services: UseCaseProvider


    init(services: UseCaseProvider,
         navigationController: UINavigationController,
         storyBoard: UIStoryboard) {
        self.services = services
        self.navigationController = navigationController
        self.storyBoard = storyBoard
    }
    
    func toPosts() {
        let vc = storyBoard.instantiateViewController(ofType: PostsViewController.self)
        vc.viewModel = PostsViewModel(useCase: services.makePostsUseCase(),
                                      navigator: self)
        navigationController.pushViewController(vc, animated: true)
    }
}

Phân tích:

Như vậy, tại Application, bằng việc khởi tạo những biến cụ thể realmUseCaseProvider = RealmPlatform.UseCaseProvider(). Thông qua dòng code này, ta đã đi đến được RealmPlatform. Bây giờ là việc đưa PostsUseCase() vào đến tận chân răng (tức là ViewModel + ViewController) thì đã có DefaultPostsNavigator. Sau khi khởi tạo VC, nó khởi tạo luôn ViewModel đồng thời gắn luôn service từ RealmPlatform sang. Như vậy useCase ở ViewController hiện tại sẽ được gọi từ PostUseCase thuộc RealmPlatform. 

3. Đúc kết:

Như vậy, có thể thấy việc thay đổi, sử dụng framework gì bên Platform hầu như chẳng ảnh hưởng gì đến bên Application và Domain cả. Giả sử khách hàng bây giờ không muốn mình làm CoreData nữa mà chuyển qua làm Realm. Ok! Đổi thì đổi, thay vài dòng code ở đoạn set UseCaseProvider cho người mới xong rồi việc cần làm là implementation bên Platform. Một cái lợi nữa là mạnh ai người đấy làm, ví dụ như useCase trong View Model ở trên chẳng cần quan tâm Platform làm cách nào, nó chỉ cần biết là nó đã yêu cầu phải trả ra một Observable<[Post]>. Cứ thế mà nó làm thôi.
Như vậy là xong :D Mình sẽ tiếp tục. Nếu bạn đã đọc đến đây, mình rất cảm kích vì sự đồng hành của bạn ^^. 

P/s: Các bạn có thể ủng hộ mình tại đây: https://vrdonate.vn/1529835412qxhvh#. ^^ .

Bình luận
* Các email sẽ không được công bố trên trang web.
I BUILT MY SITE FOR FREE USING