Tôi xác nhận bạn giải thích đúng, vì tôi cũng đang làm kiến trúc này trong dự án Mobile. Bổ sung thêm phần presentation sẽ là MVVM, MVP hay MVC. Và nó gọi vào class Usecase implement kế thừa IUsecase.
Khi mình implement ở layer usercase mình sẽ define IRepository và IUserCase, còn data layer ở tầng low level nó phải import interface repo từ usercase phải k vậy?
interface IRepositoryPlayer { getAllPlayer: () => Promise; } class RepositoryPlayer implements IRepositoryPlayer { getAllPlayer() { // get from DB return Promise.resolve(["thuong"]); } } class PlayerService { constructor(private repository: IRepositoryPlayer) {} } const repoPlayer = new RepositoryPlayer(); const player = new PlayerService(repoPlayer); Theo em hiểu cái đoạn 9:11 sẽ là ntn đúng k a ạ ?.
A ơi! em bị yếu về mấy thứ kiến trúc này. Em xem nhiều mà không hiểu rõ. Em nghĩ là em nên học lại cho chắc phần kiến trúc. Anh cho em lời khuyên em nên bắt đầu từ đâu ạ
cho e hỏi thêm với ạ, ở phút 9:00 em thấy ý tưởng đặt `IRepository` ở tầng usecase của a rất hay. nhưng nếu làm theo cách này thì tầng data ở dưới phải import ngược lên module usecase => data layer nó phụ thuộc vào usecase layer rồi đúng k ạ, theo e ĐÚNG phải là tầng usecase phụ thuộc vào tầng data ạ. Có cách nào giải quyết vấn đề này không ạ?
Em nghe lại đoạn nguyên lý Clean Architecture nhé. Data Layer (DB) là tầng low level (details) nên sẽ depend vào tầng high level (use case) - Dependency Inversion (lệ thuộc đảo). Nếu UseCase lệ thuộc vào Data Layer sẽ khiến UseCase sẽ thay đổi khi DL thay đổi. Việc này vi phạm nguyên lý DI.
dạ cho e hỏi ạ. cái dialog 4:40 . trường hợp trong project chỉ có duy nhất một 1 implementation của contract `IUseCase` và tầng Data cũng chỉ có duy nhất 1 implementation của `IRepository` thì có nhất thiết phải áp dụng DI, tạo ra 2 interface chỗ này không ạ? em thấy đang phức tạp hoá 1 vấn đề đơn giản ạ. Vì theo em hiểu mình dùng Interface để có thể thay đổi một cách dynamic at runtime mà trong project chỉ có duy nhất 1 implementation của contract đó thì "WITHOUT DI" chỗ này cũng giải quyết tốt vấn đề ạ!
Dù chỉ có một implement duy nhất nhưng nếu ko abstraction qua interface thì: - Tightly Coupling giữa các object. Tương lai nếu cần thay đổi repo là phải đổi code ở 2 object. VD đơn giản là caching hoặc đổi db engine, hoặc chia read/write. - Vì gọi trực tiếp thì sẽ gần như ko mock để Unit Testing đc, buộc phải mở real connection để test (integration). Nếu chương trình không bao giờ thay đổi, test một lần duy nhất thì ko cần tới kiến trúc.
Em vẫn đang chưa hiểu chút ở đoạn 6:30 phần business logic implements IUsecase như nào anh ạ? Tức là IUsecase sẽ defind những function rồi Business logic sẽ implments IUsecase đó phải không anh?
Em chào Anh, Anh có thể giải thích giúp em Presenters layer implement cái gì được không ạ? +, Flow từ: UI -> presenters -> use case Em không biết presenters có chức năng gì trong flow này. Cùng với đó là flow of control ở sơ đồ góc dưới dùng bên phải với ạ: +, Controller -> use case interactor -> presenters. Vì theo dependence rule thì low level sẽ depend vào high level nên em không hiểu cái flow này đi như nào cả. Em mới tìm hiểu về clean nên mong Anh thông cảm vì ngôn từ có thể không được chuẩn chỉnh ạ. Em cảm ơn Anh nhiều.
Để dễ phân biệt, presenter thường đại diện cho view, hoặc view model (dạng dữ liệu, modeling dành cho view). UI có thể dựa trên view model để render. Thông thường chúng ta sẽ thấy presenter là một interface, và view là một implement của nó. Ở tầng này sẽ xử lý render UI, events,... Mục đích là 1 view model sẽ được thể hiện dưới nhiều view cụ thể khác nhau. Controller là tầng thường dùng để xử lý logic. Theo clean thì controller và presenter là chung một cấp (level/layer). Chẳng qua là chúng khác biệt về nhiệm vụ. Flow của clean nếu trong một application có UI thường sẽ là: UI -> controller (thường gọi là input port) -> Use Case -> Presenter (output port) - vì view đang kết nối với presenter nên khi presenter thay đổi sẽ khiến UI thay đổi theo. Output port ở đây cần hiểu rộng ra là nó có thể là một presenter (liên quan tới view model) hoặc một persistent (database, storage), remote services. Presenter, Controller đều là low level so với Use Case. Để biết điểm gọi vào, chúng ta chỉ cần xem input port là gì. Trong ảnh kiến trúc clean (góc phải bên dưới), ta có Controller chính là input còn presenter là output.
@@viettx tức là cái flow và cái dependence rule là 2 cái hoàn toàn khác nhau phải không anh? Phải chăng em nhầm lẫn giữa flow và Dependence rule là một nên em mới hỏi như vậy?
Nhiều mà, từ template tới demo ở rất nhiều ngôn ngữ, a cũng có một demo nho nhỏ cho cả Clean Architecture và Miroservices: github.com/viettranx/micro-clean-architecture-service-demo
@@viettx a Việt ơi, nếu theo demo của anh, thì e dùng "go mockery" để gen test mock, thì ở folder business sẽ không có mock interface của chính tầng business đó. và cái test mock được gen bởi "go mockery" chính là test mock của folder storage. a có thể hướng dẫn viết test theo kiến trúc mà a refer được không ạ. e cảm ơn ạ.
Clip hướng dẫn implement Clean Architecture: ruclips.net/video/Y_Te5Q3gxMI/видео.html
Mình đọc nhiều về các loại architecture rồi, nhưng khi xem clip này mới thấy thực sự ưng ý.
Em là sinh viên mới ra trường, mặc dù chưa đi làm nhưng lại hứng thú với các thành phần kiến trúc như thế này, cảm ơn anh đã chia sẻ.
Tôi xác nhận bạn giải thích đúng, vì tôi cũng đang làm kiến trúc này trong dự án Mobile. Bổ sung thêm phần presentation sẽ là MVVM, MVP hay MVC. Và nó gọi vào class Usecase implement kế thừa IUsecase.
Kiến thức anh chia sẻ hay và dễ hiểu +1 respect ạ!
Cho e hỏi Clean Architecture khác gì với Hexagonal Architecture vậy ạ
Cám ơn những kiến thức bổ ích mà anh đã chia sẻ.
Khi mình implement ở layer usercase mình sẽ define IRepository và IUserCase, còn data layer ở tầng low level nó phải import interface repo từ usercase phải k vậy?
anh ơi! giải thích thêm về domain driven design đi ak.
interface IRepositoryPlayer {
getAllPlayer: () => Promise;
}
class RepositoryPlayer implements IRepositoryPlayer {
getAllPlayer() {
// get from DB
return Promise.resolve(["thuong"]);
}
}
class PlayerService {
constructor(private repository: IRepositoryPlayer) {}
}
const repoPlayer = new RepositoryPlayer();
const player = new PlayerService(repoPlayer);
Theo em hiểu cái đoạn 9:11 sẽ là ntn đúng k a ạ ?.
Yes e
A ơi! em bị yếu về mấy thứ kiến trúc này. Em xem nhiều mà không hiểu rõ. Em nghĩ là em nên học lại cho chắc phần kiến trúc. Anh cho em lời khuyên em nên bắt đầu từ đâu ạ
cho e hỏi thêm với ạ, ở phút 9:00 em thấy ý tưởng đặt `IRepository` ở tầng usecase của a rất hay. nhưng nếu làm theo cách này thì tầng data ở dưới phải import ngược lên module usecase => data layer nó phụ thuộc vào usecase layer rồi đúng k ạ, theo e ĐÚNG phải là tầng usecase phụ thuộc vào tầng data ạ. Có cách nào giải quyết vấn đề này không ạ?
Em nghe lại đoạn nguyên lý Clean Architecture nhé. Data Layer (DB) là tầng low level (details) nên sẽ depend vào tầng high level (use case) - Dependency Inversion (lệ thuộc đảo).
Nếu UseCase lệ thuộc vào Data Layer sẽ khiến UseCase sẽ thay đổi khi DL thay đổi. Việc này vi phạm nguyên lý DI.
dạ cho e hỏi ạ. cái dialog 4:40 . trường hợp trong project chỉ có duy nhất một 1 implementation của contract `IUseCase` và tầng Data cũng chỉ có duy nhất 1 implementation của `IRepository` thì có nhất thiết phải áp dụng DI, tạo ra 2 interface chỗ này không ạ? em thấy đang phức tạp hoá 1 vấn đề đơn giản ạ. Vì theo em hiểu mình dùng Interface để có thể thay đổi một cách dynamic at runtime mà trong project chỉ có duy nhất 1 implementation của contract đó thì "WITHOUT DI" chỗ này cũng giải quyết tốt vấn đề ạ!
Dù chỉ có một implement duy nhất nhưng nếu ko abstraction qua interface thì:
- Tightly Coupling giữa các object. Tương lai nếu cần thay đổi repo là phải đổi code ở 2 object. VD đơn giản là caching hoặc đổi db engine, hoặc chia read/write.
- Vì gọi trực tiếp thì sẽ gần như ko mock để Unit Testing đc, buộc phải mở real connection để test (integration).
Nếu chương trình không bao giờ thay đổi, test một lần duy nhất thì ko cần tới kiến trúc.
Với e coi thêm clip này nhé: ruclips.net/video/Y_Te5Q3gxMI/видео.html
@@viettx dạ cảm ơn anh, em hiểu rồi ạ.
Em vẫn đang chưa hiểu chút ở đoạn 6:30 phần business logic implements IUsecase như nào anh ạ? Tức là IUsecase sẽ defind những function rồi Business logic sẽ implments IUsecase đó phải không anh?
Clip hướng dẫn implement Clean Architecture và mock test: ruclips.net/video/Y_Te5Q3gxMI/видео.html
Nice voice, nice content. I respect you. By the way, I've subcribed and I think that I'll follow you in the future.
Em chào Anh, Anh có thể giải thích giúp em Presenters layer implement cái gì được không ạ?
+, Flow từ: UI -> presenters -> use case
Em không biết presenters có chức năng gì trong flow này.
Cùng với đó là flow of control ở sơ đồ góc dưới dùng bên phải với ạ:
+, Controller -> use case interactor -> presenters.
Vì theo dependence rule thì low level sẽ depend vào high level nên em không hiểu cái flow này đi như nào cả.
Em mới tìm hiểu về clean nên mong Anh thông cảm vì ngôn từ có thể không được chuẩn chỉnh ạ.
Em cảm ơn Anh nhiều.
Để dễ phân biệt, presenter thường đại diện cho view, hoặc view model (dạng dữ liệu, modeling dành cho view). UI có thể dựa trên view model để render. Thông thường chúng ta sẽ thấy presenter là một interface, và view là một implement của nó. Ở tầng này sẽ xử lý render UI, events,... Mục đích là 1 view model sẽ được thể hiện dưới nhiều view cụ thể khác nhau.
Controller là tầng thường dùng để xử lý logic. Theo clean thì controller và presenter là chung một cấp (level/layer). Chẳng qua là chúng khác biệt về nhiệm vụ.
Flow của clean nếu trong một application có UI thường sẽ là:
UI -> controller (thường gọi là input port) -> Use Case -> Presenter (output port) - vì view đang kết nối với presenter nên khi presenter thay đổi sẽ khiến UI thay đổi theo.
Output port ở đây cần hiểu rộng ra là nó có thể là một presenter (liên quan tới view model) hoặc một persistent (database, storage), remote services.
Presenter, Controller đều là low level so với Use Case. Để biết điểm gọi vào, chúng ta chỉ cần xem input port là gì. Trong ảnh kiến trúc clean (góc phải bên dưới), ta có Controller chính là input còn presenter là output.
@@viettx tức là cái flow và cái dependence rule là 2 cái hoàn toàn khác nhau phải không anh? Phải chăng em nhầm lẫn giữa flow và Dependence rule là một nên em mới hỏi như vậy?
@@Kai-wi1md yes. Dependency là "lệ thuộc", không phải call flow.
@@viettx em cảm ơn anh nhiều.
Cảm giác nghe nó hơi bị trừu tượng sao ấy anh Việt. Em nghĩ nếu có example demo thì sẽ tường minh hơn về kiến trúc ạ
Example sẽ là code implement mất rồi e ạ. Quan trọng nhất vẫn là dependency rule và isolate business logic thôi à.
Clip hướng dẫn implement Clean Architecture: ruclips.net/video/Y_Te5Q3gxMI/видео.html
Nói xuông vậy thì em xem nhiều rồi. Chưa thấy ai bắt tay xây dựng một cái project giống y như vậy cho dễ hiểu đó anh @@
Nhiều mà, từ template tới demo ở rất nhiều ngôn ngữ, a cũng có một demo nho nhỏ cho cả Clean Architecture và Miroservices: github.com/viettranx/micro-clean-architecture-service-demo
@@viettx anh làm 1 video step by step đi anh
@@viettx a Việt ơi, nếu theo demo của anh, thì e dùng "go mockery" để gen test mock, thì ở folder business sẽ không có mock interface của chính tầng business đó.
và cái test mock được gen bởi "go mockery" chính là test mock của folder storage.
a có thể hướng dẫn viết test theo kiến trúc mà a refer được không ạ.
e cảm ơn ạ.
@@lewis.chien_ e check lại nhánh master vì a có refactor lại bỏ đi storage cho đơn giản và fit CA hơn
@@lewis.chien_ e mock cái repository để unit test cho business nha.
cảm ơn bạn