Trong những năm qua, ngành công nghiệp phần mềm di động
đã phát triển với một tốc độ chóng mặt. Trước đây các ứng dụng di động chủ yếu là nhỏ và thường chứa ít màn hình. Trong khi bây giờ có rất nhiều ứng dụng khổng lồ, với giao diện người dùng và business logic
rất phức tạp.
Đối với các ứng dụng phức tạp thì mô hình MVC
truyền thống bộc lộ nhiều khuyết điểm như: View
và Controller
có mối quan hệ chặt chẽ với nhau đến mức mà phần Model gần như bị tách biệt làm cho việc Testing rất khó khăn, ViewController phình to vì chưa cả logic bussiness lẫn logic view làm cho việc đọc hiểu code để bảo trì và mở rộng rất khó khăn… Clean Architecture
đã ra đời để giải quyết các vấn đề trên bằng việc phân tách rõ ràng các module dựa trên các nhiệm vụ riêng biệt.
Clean Architecture
Clean Architecture
được giới thiệu bởi Robert C. Martin (a.k.a. Chú Bob) và nhận được sự quan tâm lớn của các developer. Clean Architecture
là một business architecture, nó tách rời những xử lý nghiệp vụ khỏi UI và framework, phân rõ vai trò và trách nhiệm của từng layer trong kiến trúc của mình.
The Anatomy of the Clean Architecture
Có rất nhiều biến thể của Clean Architecture
, trong iOS bạn có thể nghe nói về VIPER
hoặc CleanSwift
. Khi nhảy sang các nền tảng khác như .NET
hay Android
thậm chí còn có nhiều biến thể hơn. Tuy nhiên các biến thể đều có nhiệm vụ như nhau là tách rời những xử lý nghiệp vụ khỏi UI và framework và tuân thủ theo các qui tắc của Clean Architecture
.
Dưới đây là danh sách các thành phần trong kiến trúc:
- View: Giao diện hiển thị nơi xảy ra tương tác giữa app và người dùng, như Storyboard hoặc XIB.
- Controller: Nhận các hành động hoặc event từ view và update nó.
- Interactor: Lớp logic nghiệp vụ nơi Controller gửi các yêu cầu.
- Presenter: Nhận phản hổi từ Interactor để gửi lại cho Controller.
- Router: Có nhiệm vụ điều hướng các ViewController.
Phần cốt lõi của kiến trúc là Controller, Interactor, và Presenter. Một điều quan trọng cần lưu ý đây là kiến trúc unidirectional Data Flow
tức là dữ liệu sẽ di chuyển theo 1 luồng xác định, điều này làm giảm đáng kể sự phức tạp, dễ dàng để quản lý.
Cách hoạt động:
- Người dùng tương tác với View
- Controller nhận sự kiện từ View để gửi đến Interactor.
- Interactor thực hiện các logic business và trả về kết quả cho Presenter
- Presenter format lại dữ liệu sau đó gửi lại về cho Controller thông qua viewModel
- Controller nhận dữ liệu từ Presenter sau đó update lại view.
Practice
Chúng ta định nghĩa các use-case cho từng layer tương ứng
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token keyword">protocol</span> <span class="token builtin">ListProductsDisplayable</span><span class="token punctuation">:</span> <span class="token keyword">class</span> <span class="token punctuation">{</span> <span class="token comment">// View Controller</span> <span class="token keyword">func</span> <span class="token function">displayFetchedProducts</span><span class="token punctuation">(</span>with viewModel<span class="token punctuation">:</span> <span class="token builtin">ListProductsModels</span><span class="token punctuation">.</span><span class="token builtin">ViewModel</span><span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">display</span><span class="token punctuation">(</span>error<span class="token punctuation">:</span> <span class="token builtin">AppModels</span><span class="token punctuation">.</span><span class="token builtin">Error</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">protocol</span> <span class="token builtin">ListProductsBusinessLogic</span> <span class="token punctuation">{</span> <span class="token comment">// Interactor</span> <span class="token keyword">func</span> <span class="token function">fetchProducts</span><span class="token punctuation">(</span>with request<span class="token punctuation">:</span> <span class="token builtin">ListProductsModels</span><span class="token punctuation">.</span><span class="token builtin">FetchRequest</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">protocol</span> <span class="token builtin">ListProductsPresentable</span> <span class="token punctuation">{</span> <span class="token comment">// Presenter</span> <span class="token keyword">func</span> <span class="token function">presentFetchedProducts</span><span class="token punctuation">(</span><span class="token keyword">for</span> response<span class="token punctuation">:</span> <span class="token builtin">ListProductsModels</span><span class="token punctuation">.</span><span class="token builtin">Response</span><span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">presentFetchedProducts</span><span class="token punctuation">(</span>error<span class="token punctuation">:</span> <span class="token builtin">DataError</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">protocol</span> <span class="token builtin">ListProductsRoutable</span><span class="token punctuation">:</span> <span class="token builtin">AppRoutable</span> <span class="token punctuation">{</span> <span class="token comment">// Router</span> <span class="token keyword">func</span> <span class="token function">showProduct</span><span class="token punctuation">(</span><span class="token keyword">for</span> id<span class="token punctuation">:</span> <span class="token builtin">Int</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Đây là flow xảy ra tại viewDidload interactor.fetchProducts > presenter.presentFetchedProducts > controller.displayFetchedProducts.
Controller :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | <span class="token keyword">class</span> <span class="token class-name">ListProductsViewController</span><span class="token punctuation">:</span> <span class="token builtin">UIViewController</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">lazy</span> <span class="token keyword">var</span> interactor<span class="token punctuation">:</span> <span class="token builtin">ListProductsBusinessLogic</span> <span class="token operator">=</span> <span class="token function">ListProductsInteractor</span><span class="token punctuation">(</span> presenter<span class="token punctuation">:</span> <span class="token function">ListProductsPresenter</span><span class="token punctuation">(</span>viewController<span class="token punctuation">:</span> <span class="token keyword">self</span><span class="token punctuation">)</span><span class="token punctuation">,</span> productsWorker<span class="token punctuation">:</span> <span class="token function">ProductsWorker</span><span class="token punctuation">(</span>store<span class="token punctuation">:</span> <span class="token function">ProductsMemoryStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token keyword">private</span> <span class="token keyword">lazy</span> <span class="token keyword">var</span> router<span class="token punctuation">:</span> <span class="token builtin">ListProductsRoutable</span> <span class="token operator">=</span> <span class="token function">ListProductsRouter</span><span class="token punctuation">(</span> viewController<span class="token punctuation">:</span> <span class="token keyword">self</span> <span class="token punctuation">)</span> <span class="token keyword">private</span> <span class="token keyword">var</span> viewModel<span class="token punctuation">:</span> <span class="token builtin">ListProductsModels</span><span class="token punctuation">.</span><span class="token builtin">ViewModel</span><span class="token operator">?</span> <span class="token keyword">override</span> <span class="token keyword">func</span> <span class="token function">viewDidLoad</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">viewDidLoad</span><span class="token punctuation">(</span><span class="token punctuation">)</span> interactor<span class="token punctuation">.</span><span class="token function">fetchProducts</span><span class="token punctuation">(</span> with<span class="token punctuation">:</span> <span class="token builtin">ListProductsModels</span><span class="token punctuation">.</span><span class="token function">FetchRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">ListProductsViewController</span><span class="token punctuation">:</span> <span class="token builtin">ListProductsDisplayable</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">displayFetchedProducts</span><span class="token punctuation">(</span>with viewModel<span class="token punctuation">:</span> <span class="token builtin">ListProductsModels</span><span class="token punctuation">.</span><span class="token builtin">ViewModel</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span><span class="token punctuation">.</span>viewModel <span class="token operator">=</span> viewModel tableView<span class="token punctuation">.</span><span class="token function">reloadData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">display</span><span class="token punctuation">(</span>error<span class="token punctuation">:</span> <span class="token builtin">AppModels</span><span class="token punctuation">.</span><span class="token builtin">Error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> alertController <span class="token operator">=</span> <span class="token function">UIAlertController</span><span class="token punctuation">(</span> title<span class="token punctuation">:</span> error<span class="token punctuation">.</span>title<span class="token punctuation">,</span> message<span class="token punctuation">:</span> error<span class="token punctuation">.</span>message<span class="token punctuation">,</span> preferredStyle<span class="token punctuation">:</span> <span class="token punctuation">.</span>alert <span class="token punctuation">)</span> alertController<span class="token punctuation">.</span><span class="token function">addAction</span><span class="token punctuation">(</span> <span class="token function">UIAlertAction</span><span class="token punctuation">(</span>title<span class="token punctuation">:</span> <span class="token string">"OK"</span><span class="token punctuation">,</span> style<span class="token punctuation">:</span> <span class="token punctuation">.</span><span class="token keyword">default</span><span class="token punctuation">,</span> handler<span class="token punctuation">:</span> <span class="token constant">nil</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token function">present</span><span class="token punctuation">(</span>alertController<span class="token punctuation">,</span> animated<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> completion<span class="token punctuation">:</span> <span class="token constant">nil</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">ListProductsViewController</span><span class="token punctuation">:</span> <span class="token builtin">UITableViewDelegate</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">tableView</span><span class="token punctuation">(</span><span class="token number">_</span> tableView<span class="token punctuation">:</span> <span class="token builtin">UITableView</span><span class="token punctuation">,</span> didSelectRowAt indexPath<span class="token punctuation">:</span> <span class="token builtin">IndexPath</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> model <span class="token operator">=</span> viewModel<span class="token operator">?</span><span class="token punctuation">.</span>products<span class="token punctuation">[</span>indexPath<span class="token punctuation">.</span>row<span class="token punctuation">]</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> router<span class="token punctuation">.</span><span class="token function">showProduct</span><span class="token punctuation">(</span><span class="token keyword">for</span><span class="token punctuation">:</span> model<span class="token punctuation">.</span>id<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Controller tạo một router và một instances đến Interactor thông qua 1 presenter. Gọi Interactor ở viewDidload
để fetch list product.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token keyword">struct</span> <span class="token builtin">ListProductsInteractor</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">let</span> presenter<span class="token punctuation">:</span> <span class="token builtin">ListProductsPresentable</span> <span class="token keyword">private</span> <span class="token keyword">let</span> productsWorker<span class="token punctuation">:</span> <span class="token builtin">ProductsWorkerType</span> <span class="token keyword">init</span><span class="token punctuation">(</span>presenter<span class="token punctuation">:</span> <span class="token builtin">ListProductsPresentable</span><span class="token punctuation">,</span> productsWorker<span class="token punctuation">:</span> <span class="token builtin">ProductsWorkerType</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span><span class="token punctuation">.</span>presenter <span class="token operator">=</span> presenter <span class="token keyword">self</span><span class="token punctuation">.</span>productsWorker <span class="token operator">=</span> productsWorker <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">ListProductsInteractor</span><span class="token punctuation">:</span> <span class="token builtin">ListProductsBusinessLogic</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">fetchProducts</span><span class="token punctuation">(</span>with request<span class="token punctuation">:</span> <span class="token builtin">ListProductsModels</span><span class="token punctuation">.</span><span class="token builtin">FetchRequest</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> productsWorker<span class="token punctuation">.</span>fetch <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> value <span class="token operator">=</span> $<span class="token number">0</span><span class="token punctuation">.</span>value<span class="token punctuation">,</span> $<span class="token number">0</span><span class="token punctuation">.</span>isSuccess <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">self</span><span class="token punctuation">.</span>presenter<span class="token punctuation">.</span><span class="token function">presentFetchedProducts</span><span class="token punctuation">(</span>error<span class="token punctuation">:</span> $<span class="token number">0</span><span class="token punctuation">.</span>error <span class="token operator">?</span><span class="token operator">?</span> <span class="token punctuation">.</span><span class="token function">unknownReason</span><span class="token punctuation">(</span><span class="token constant">nil</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">self</span><span class="token punctuation">.</span>presenter<span class="token punctuation">.</span><span class="token function">presentFetchedProducts</span><span class="token punctuation">(</span> <span class="token keyword">for</span><span class="token punctuation">:</span> <span class="token builtin">ListProductsModels</span><span class="token punctuation">.</span><span class="token function">Response</span><span class="token punctuation">(</span>products<span class="token punctuation">:</span> value<span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Inject 1 productsWorker
ở Interactor để xử lý API.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <span class="token keyword">struct</span> <span class="token builtin">ListProductsPresenter</span><span class="token punctuation">:</span> <span class="token builtin">ListProductsPresentable</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> viewController<span class="token punctuation">:</span> <span class="token builtin">ListProductsDisplayable</span><span class="token operator">?</span> <span class="token keyword">private</span> <span class="token keyword">let</span> currencyFormatter<span class="token punctuation">:</span> <span class="token builtin">NumberFormatter</span> <span class="token keyword">init</span><span class="token punctuation">(</span>viewController<span class="token punctuation">:</span> <span class="token builtin">ListProductsDisplayable</span><span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span><span class="token punctuation">.</span>viewController <span class="token operator">=</span> viewController <span class="token keyword">self</span><span class="token punctuation">.</span>currencyFormatter <span class="token operator">=</span> <span class="token function">NumberFormatter</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">self</span><span class="token punctuation">.</span>currencyFormatter<span class="token punctuation">.</span>numberStyle <span class="token operator">=</span> <span class="token punctuation">.</span>currency <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">ListProductsPresenter</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">presentFetchedProducts</span><span class="token punctuation">(</span><span class="token keyword">for</span> response<span class="token punctuation">:</span> <span class="token builtin">ListProductsModels</span><span class="token punctuation">.</span><span class="token builtin">Response</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> viewModel <span class="token operator">=</span> <span class="token builtin">ListProductsModels</span><span class="token punctuation">.</span><span class="token function">ViewModel</span><span class="token punctuation">(</span> products<span class="token punctuation">:</span> response<span class="token punctuation">.</span>products<span class="token punctuation">.</span><span class="token builtin">map</span> <span class="token punctuation">{</span> <span class="token builtin">ListProductsModels</span><span class="token punctuation">.</span><span class="token function">ProductViewModel</span><span class="token punctuation">(</span> id<span class="token punctuation">:</span> $<span class="token number">0</span><span class="token punctuation">.</span>id<span class="token punctuation">,</span> name<span class="token punctuation">:</span> $<span class="token number">0</span><span class="token punctuation">.</span>name<span class="token punctuation">,</span> content<span class="token punctuation">:</span> $<span class="token number">0</span><span class="token punctuation">.</span>content<span class="token punctuation">,</span> price<span class="token punctuation">:</span> currencyFormatter<span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span>from<span class="token punctuation">:</span> <span class="token function">NSNumber</span><span class="token punctuation">(</span>value<span class="token punctuation">:</span> <span class="token function">Float</span><span class="token punctuation">(</span>$<span class="token number">0</span><span class="token punctuation">.</span>priceCents<span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">?</span><span class="token operator">?</span> <span class="token string">"<span class="token interpolation"><span class="token delimiter variable">(</span>$<span class="token number">0</span><span class="token punctuation">.</span>priceCents <span class="token operator">/</span> <span class="token number">100</span><span class="token delimiter variable">)</span></span>"</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> viewController<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">displayFetchedProducts</span><span class="token punctuation">(</span>with<span class="token punctuation">:</span> viewModel<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">presentFetchedProducts</span><span class="token punctuation">(</span>error<span class="token punctuation">:</span> <span class="token builtin">DataError</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Handle and parse error</span> <span class="token keyword">let</span> viewModel <span class="token operator">=</span> <span class="token builtin">AppModels</span><span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span> title<span class="token punctuation">:</span> <span class="token function">NSLocalizedString</span><span class="token punctuation">(</span><span class="token string">"products.error.title"</span><span class="token punctuation">,</span> <span class="token string">"Title for product error"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> message<span class="token punctuation">:</span> <span class="token function">String</span><span class="token punctuation">(</span>format<span class="token punctuation">:</span> <span class="token function">NSLocalizedString</span><span class="token punctuation">(</span><span class="token string">"products.error.message"</span><span class="token punctuation">,</span> <span class="token string">"Message for product error"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> error<span class="token punctuation">)</span> <span class="token punctuation">)</span> viewController<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">display</span><span class="token punctuation">(</span>error<span class="token punctuation">:</span> viewModel<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Presenter
sẽ làm nhiệm vụ format lại dữ liệu respone và gọi controller để hiện thị lên View
Các Model
được gói gọn trong một enum và chỉ liên quan đến trường hợp sử dụng riêng của nó.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token keyword">enum</span> <span class="token builtin">ListProductsModels</span> <span class="token punctuation">{</span> <span class="token keyword">struct</span> <span class="token builtin">FetchRequest</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">struct</span> <span class="token builtin">SearchRequest</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> text<span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token punctuation">}</span> <span class="token keyword">struct</span> <span class="token builtin">Response</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> products<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token builtin">ProductType</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token keyword">struct</span> <span class="token builtin">ViewModel</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> products<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token builtin">ProductViewModel</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token keyword">struct</span> <span class="token builtin">ProductViewModel</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> id<span class="token punctuation">:</span> <span class="token builtin">Int</span> <span class="token keyword">let</span> name<span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token keyword">let</span> content<span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token keyword">let</span> price<span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Cuối cùng Router
chịu trách nhiệm điều khiển các luồng đi của ứng dụng:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token keyword">struct</span> <span class="token builtin">ListProductsRouter</span> <span class="token punctuation">{</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> viewController<span class="token punctuation">:</span> <span class="token builtin">UIViewController</span><span class="token operator">?</span> <span class="token keyword">init</span><span class="token punctuation">(</span>viewController<span class="token punctuation">:</span> <span class="token builtin">UIViewController</span><span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span><span class="token punctuation">.</span>viewController <span class="token operator">=</span> viewController <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">ListProductsRouter</span><span class="token punctuation">:</span> <span class="token builtin">ListProductsRoutable</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">showProduct</span><span class="token punctuation">(</span><span class="token keyword">for</span> id<span class="token punctuation">:</span> <span class="token builtin">Int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> storyboard <span class="token operator">=</span> <span class="token function">UIStoryboard</span><span class="token punctuation">(</span>name<span class="token punctuation">:</span> <span class="token string">"ShowProduct"</span><span class="token punctuation">,</span> bundle<span class="token punctuation">:</span> <span class="token constant">nil</span><span class="token punctuation">)</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> controller <span class="token operator">=</span> storyboard<span class="token punctuation">.</span><span class="token function">instantiateInitialViewController</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">as</span><span class="token operator">?</span> <span class="token builtin">ShowProductViewController</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">assertionFailure</span><span class="token punctuation">(</span><span class="token string">"Invalid controller for storyboard <span class="token interpolation"><span class="token delimiter variable">(</span>storyboard<span class="token delimiter variable">)</span></span>."</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> controller<span class="token punctuation">.</span>productID <span class="token operator">=</span> id viewController<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">present</span><span class="token punctuation">(</span>controller<span class="token punctuation">,</span> animated<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
router.showProduct(for: productID)
Conclusion
Clean architechture rất linh hoạt, dễ dàng bảo trì và mở rộng.Mặc dù nó dài dòng hơn các kiến trúc khác, nhưng nó là cần thiết để làm giảm sự phụ thuộc giứa các layer trong ứng dụng.
Link tham khảo: https://basememara.com/swift-clean-architecture/