https://www.caandesign.com/wp-content/uploads/2016/07/pitsou-kedem-architects-designed-s-house-concrete-home-modern-look-made-clean-lines-11.jpg

Kiến Trúc Sạch

Dịch từ bài viết The Clean Architecture của tác giả Robert C. Martin hay còn được biết đến rộng rãi với tên “Bác” Bob.

Over the last several years we’ve seen a whole range of ideas regarding the architecture of systems. These include:

Trong một vài năm gần đây chúng ta đã thấy một lượng lớn các ý tưởng liên quan đến kiến trúc hệ thống. Trong đó có:

* Hexagonal Architecture (a.k.a. Ports and Adapters) by Alistair Cockburn and adopted by Steve Freeman, and Nat Pryce in their wonderful book Growing Object Oriented Software
* Onion Architecture by Jeffrey Palermo
* Screaming Architecture from a blog of mine last year
* DCI from James Coplien, and Trygve Reenskaug.
* BCE by Ivar Jacobson from his book Object Oriented Software Engineering: A Use-Case Driven Approach
  • Kiến trúc Bát giác (còn được biết với với tên gọi Cổng và Tiếp chuyển) của Alistair Cockburn và đồng phát triển bởi Steve Freeman cùng với Nat Pryce trong cuốn sách rất tuyệt vời Growing Object Oriented Software.
  • Kiến trúc Củ Hành của Jeffrey Palermo.
  • Kiến trúc Tiếng Thét từ blog của tôi năm ngoái.
  • DCI từ James Coplien, và Trygve Reenskaug.
  • BCE bởi Ivar Jacobson trong cuốn sách Kỹ thuật Phần mềm Hướng Đối tượng: Một cách tiếp cận hướng tình huống.

Though these architectures all vary somewhat in their details, they are very similar. They all have the same objective, which is the separation of concerns. They all achieve this separation by dividing the software into layers. Each has at least one layer for business rules, and another for interfaces.

Mặc dù có những điểm khác nhau thế nào đó khi đi vào chi tiết, nhưng các kiến trúc trên có chung một tư duy thiết kế. Mục tiêu đều là tách bạch những khía cạnh cần quan tâm. Phương pháp đều là chia tách phần mềm thành nhiều tầng lớp. Tất cả đều có ít nhất một tầng dành cho các nghiệp vụ, và một tầng khác cho các giao diện.

Each of these architectures produce systems that are:

Tất cả đều cho xây dựng ra những hệ thống mà:

Independent of Frameworks. The architecture does not depend on the existence of some library of feature laden software. This allows you to use such frameworks as tools, rather than having to cram your system into their limited constraints.
Không phụ thuộc vào Framework

Bản thân kiến trúc không phụ thuộc vào bất kỳ thư viện hay chức năng của một phần mềm nào đang hiện hữu. Đặc điểm này này cho phép bạn sử dụng framework như một công cụ thay vì bị ràng buộc theo những giới hạn của chúng.

Testable. The business rules can be tested without the UI, Database, Web Server, or any other external element.
Kiểm thử được

Có thể kiểm thử được những quy tắc nghiệp vụ mà không cần đến UI, Database, Web Server, hay bất kỳ phần tử nào bên ngoài nào khác.

Independent of UI. The UI can change easily, without changing the rest of the system. A Web UI could be replaced with a console UI, for example, without changing the business rules.
Biệt lập với UI

UI có thể dễ dàng được thay đổi mà không lôi kéo thêm bất kỳ thay đổi trên phần còn lại của hệ thống. Lấy ví dụ, Web UI có thể được hoán đổi bởi giao diện dòng lệnh mà không gây ra sự thay đổi trên những quy tắc nghiệp vụ.

Independent of Database. You can swap out Oracle or SQL Server, for Mongo, BigTable, CouchDB, or something else. Your business rules are not bound to the database.
Không phụ thuộc Database

Bạn có thể hoán đổi cơ sở dữ liệu Oracle hay SQL bởi Mongo, BigTable, CouchDB, hay bất kỳ thứ gì khác. Quy tắc nghiệp vụ hoàn toàn không phải là thứ bị giới hạn bởi cơ sở dữ liệu.

Independent of any external agency. In fact your business rules simply don’t know anything at all about the outside world.
Không phụ thuộc bất kỳ dịch vụ bên ngoài nào

Thực tế thì những quy tắc nghiệp vụ cũng không biết thêm bất kỳ điều gì khác ngoài thế giới của nó.

The diagram at the top of this article is an attempt at integrating all these architectures into a single actionable idea.

Kiến trúc được mô tả trong sơ đồ ở đầu bài viết này là một đề án để dung hợp tất cả những kiến trúc đã được liệt kê ở trên thành một ý tưởng dùng được.

The Dependency Rule

Quy tắc về Sự Phụ Thuộc

The concentric circles represent different areas of software. In general, the further in you go, the higher level the software becomes. The outer circles are mechanisms. The inner circles are policies.

Những vòng tròn đồng tâm biểu thị những phạm vi phần mềm khác nhau. Càng vào trong, cấp của phần mềm càng cao. Những vòng tròn bên ngoài là các cơ chế (cách thức). Những vòng ở trong là các chính sách (quy tắc).

The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in the an inner circle. That includes, functions, classes. variables, or any other named software entity.

Kiến trúc này hoạt động được là nhờ có một quy tắc chung bao trùm lên nó gọi là Quy tắc Phụ thuộc. Quy tắc này phát biểu rằng những phụ thuộc mã nguồn chỉ được hướng vào phía trong. Không thứ gì của vòng trong được biết tới sự tồn tại của bất kỳ thứ gì ở vòng ngoài. Cụ thể, tên của bất kỳ thứ gì được khai báo ở vòng ngoài không bao giờ được xuất hiện tại mã của vòng trong. Bao gồm tên hàm, lớp, biến, hay bất kỳ thực thể phần mềm nào khác.

By the same token, data formats used in an outer circle should not be used by an inner circle, especially if those formats are generate by a framework in an outer circle. We don’t want anything in an outer circle to impact the inner circles.

Theo lối tương tự, những định dạng dữ liệu được dùng ở những vòng ngoài không được mang đi dùng ở những vòng phía trong, đặc biệt là những định dạng được tạo ra bởi framework ở vòng ngoài. Chúng ta không muốn bất kỳ thứ gì ở vòng ngoài tạo được sức ảnh hưởng tới vòng trong.

Entities

Các thực thể

Entities encapsulate Enterprise wide business rules. An entity can be an object with methods, or it can be a set of data structures and functions. It doesn’t matter so long as the entities could be used by many different applications in the enterprise.

Các thực thể bao gói các quy tắc nghiệp vụ của tổ chức. Một thực thể có thể là một đối tượng với các phương thức, hay một tập hợp các cấu trúc dữ liệu và các hàm. Dù là gì thì điều đó cũng không quan trọng bằng việc các thực thể có thể được sử dụng bởi rất nhiều ứng dụng khác nhau trong doanh nghiệp.

If you don’t have an enterprise, and are just writing a single application, then these entities are the business objects of the application. They encapsulate the most general and high-level rules. They are the least likely to change when something external changes. For example, you would not expect these objects to be affected by a change to page navigation, or security. No operational change to any particular application should affect the entity layer.

Nếu bạn không sở hữu một tổ chức và chỉ viết ra một chương trình đơn lập, thì những thực thể này chính là những đối tượng nghiệp vụ của chương trình. Chúng bao gói những quy tắc cấp cao tổng quát nhất. Chúng rất ít khi thay đổi theo môi trường bên ngoài. Lấy ví dụ, bạn sẽ không muốn những đối tượng này bị thay đổi theo menu điều hướng trên trang Web, hay theo triển khai bảo mật. Không tồn tại bất kỳ thay đổi nào của bất kỳ ứng dụng cụ thể nào mà lại có thể ảnh hưởng tới tầng thực thể.

Use Cases

Các Ca Sử Dụng

The software in this layer contains application specific business rules. It encapsulates and implements all of the use cases of the system. These use cases orchestrate the flow of data to and from the entities, and direct those entities to use their enterprise wide business rules to achieve the goals of the use case.

Phần mềm ở tầng này chứa những quy tắc nghiệp vụ đặc thù theo từng ứng dụng. Nó bao gói và triển khai tất cả các ca sử dụng hệ thống. Những ca sử dụng đó điều phối luồng dữ liệu đến và đi từ các thực thể, và lèo lái để thực thể sử dụng quy tắc nghiệp vụ mức tổ chức để đạt được những mục đích của ca sử dụng.

We do not expect changes in this layer to affect the entities. We also do not expect this layer to be affected by changes to externalities such as the database, the UI, or any of the common frameworks. This layer is isolated from such concerns.

Chúng ta không muốn sự thay đổi trên tầng này gây được ảnh hưởng tới các thực thể. Chúng ta cũng không muốn tầng này bị ảnh hưởng bởi các thay đổi tới từ các ngoại tác như Database, UI hay bất kỳ framework phổ biến nào. Tầng này biệt lập trước những khía cạnh đó.

We do, however, expect that changes to the operation of the application will affect the use-cases and therefore the software in this layer. If the details of a use-case change, then some code in this layer will certainly be affected.

Nhưng ngược lại, cách hoạt động của chương trình sẽ gây ảnh hưởng đến ca sử dụng. Khi mô tả về một ca sử dụng có thay đổi thì mã ở tầng này thường sẽ thay đổi theo.

Interface Adapters

Các Bộ Tiếp Chuyển Giao Diện

The software in this layer is a set of adapters that convert data from the format most convenient for the use cases and entities, to the format most convenient for some external agency such as the Database or the Web. It is this layer, for example, that will wholly contain the MVC architecture of a GUI. The Presenters, Views, and Controllers all belong in here. The models are likely just data structures that are passed from the controllers to the use cases, and then back from the use cases to the presenters and views.

Phần mềm ở tầng này là một tập hợp các bộ tiếp chuyển giúp chuyển đổi dữ liệu từ định dạng tự nhiên nhất cho các ca sử dụng và thực thể sang định dạng tự nhiên nhất cho các ngoại vụ như Datasabe hay Web. Lấy ví dụ, tầng này chính là toàn bộ phạm vi chứa kiến trúc MVC của GUI. Các Presenter, các View, các Controller, tất cả đều được nằm ở đây. Các model đơn thuần là các cấu trúc dữ liệu được chuyển từ controller tới ca sử dụng, và sau đó từ ca sử dụng tới presenter và view.

Similarly, data is converted, in this layer, from the form most convenient for entities and use cases, into the form most convenient for whatever persistence framework is being used. i.e. The Database. No code inward of this circle should know anything at all about the database. If the database is a SQL database, then all the SQL should be restricted to this layer, and in particular to the parts of this layer that have to do with the database.

Tương tự như vậy, dữ liệu được chuyển đổi trong tầng này từ hình thái tự nhiên nhất cho các thực thể và ca sử dụng thành hình thái tự nhiên nhất cho tầng lưu tồn, dù cho ở đó sử dụng khung làm việc nào ví dụ như Database. Không mã nào gọi đến vòng này được biết về cơ sở dữ liệu. Nếu cơ sở dữ liệu là một SQL database thì toàn bộ SQL phải được đặt trong tầng này, và chỉ trong những mảnh chuyên biệt làm nhiệm vụ giao tiếp với cơ sở dữ liệu thôi.

Also in this layer is any other adapter necessary to convert data from some external form, such as an external service, to the internal form used by the use cases and entities.

Bên cạnh đó trong tầng này còn có mọi bộ tiếp chuyển cần thiết để chuyển đổi dữ liệu từ một hình thái ngoại lai nào đó, ví dụ như từ một dịch vụ ngoài, thành hình thái nội bộ mà các ca sử dụng và các thực thể có thể dùng.

Frameworks and Drivers.

Các framework và driver.

The outermost layer is generally composed of frameworks and tools such as the Database, the Web Framework, etc. Generally you don’t write much code in this layer other than glue code that communicates to the next circle inwards.

Tầng ngoài cùng một cách tổng quan được tạo nên từ các framework và công cụ như Database, Web Framework… Nói chung bạn không viết quá nhiều mã vào tầng này ngoại trừ những mã kết dính làm nhiệm vụ giao tiếp với tầng trong.

This layer is where all the details go. The Web is a detail. The database is a detail. We keep these things on the outside where they can do little harm.

Tầng này là nơi những chi tiết xuất hiện. Web là một chi tiết. Cơ sở dữ liệu là một chi tiết. Chúng ta giữ những chi tiết ở vòng ngoài, nơi mà chúng có thể gây ra ít ảnh hưởng nhất.

Only Four Circles?

Chỉ Bốn?

No, the circles are schematic. You may find that you need more than just these four. There’s no rule that says you must always have just these four. However, The Dependency Rule always applies. Source code dependencies always point inwards. As you move inwards the level of abstraction increases. The outermost circle is low level concrete detail. As you move inwards the software grows more abstract, and encapsulates higher level policies. The inner most circle is the most general.

Không, hình trên chỉ là giản đồ. Bạn sẽ có lúc thấy cần nhiều hơn bốn. Không có quy tắc nào giới hạn bạn chi được dùng những vòng nói tới ở đây. Nhưng Quy tắc Phụ thuộc phải được tuân thủ. Các phụ thuộc mã chỉ được hướng vào trong. Càng vào trong mức trừu tượng càng tăng lên. Vòng ngoài cùng là những chi tiết ở mức thấp. Càng đi vào trong phần mềm càng trừu tượng hơn và bao gói những chính sách ở mức cao hơn. Vòng trong cùng là nơi có mức khái quát cao nhất.

Crossing boundaries

Vượt biên

At the lower right of the diagram is an example of how we cross the circle boundaries. It shows the Controllers and Presenters communicating with the Use Cases in the next layer. Note the flow of control. It begins in the controller, moves through the use case, and then winds up executing in the presenter. Note also the source code dependencies. Each one of them points inwards towards the use cases.

Ở góc dưới bên phải của sơ đồ là một ví dụ về cách chúng ta vượt qua biên giới giữa các tầng. Nó nêu ra trường hợp các controller và presenter giao tiếp với các tầng các ca sử dụng ở trong. Luồng điều khiển đi từ controller tới các ca sử dụng, rồi quay ngoắt lại presenter. Trong khi tất cả các phụ thuộc mã nguồn vẫn tuân thủ luật phụ thuộc, hướng vào trong, tới các ca sử dụng.

We usually resolve this apparent contradiction by using the Dependency Inversion Principle. In a language like Java, for example, we would arrange interfaces and inheritance relationships such that the source code dependencies oppose the flow of control at just the right points across the boundary.

Mối mâu thuẫn rành rành đó thường được giải quyết bằng Nguyên tắc Đảo ngược Phụ thuộc. Chẳng hạn, trong ngôn ngữ Java, chúng ta có thể đặt các phụ thuộc trong mã nguồn ngược với luồng điều khiển ngay tại những điểm vượt biên.

For example, consider that the use case needs to call the presenter. However, this call must not be direct because that would violate The Dependency Rule: No name in an outer circle can be mentioned by an inner circle. So we have the use case call an interface (Shown here as Use Case Output Port) in the inner circle, and have the presenter in the outer circle implement it.

Ví dụ, giả sử ca sử dụng cần gọi presenter. Không thể thực hiện trực tiếp được bởi Nguyên tắc Phụ thuộc. Vậy nên chúng ta làm cho ca sử dụng gọi tới một interface (Use Case Output Port) đặt tại vòng trong, và cho triển khai interface tại presenter nằm ở vòng ngoài.

The same technique is used to cross all the boundaries in the architectures. We take advantage of dynamic polymorphism to create source code dependencies that oppose the flow of control so that we can conform to The Dependency Rule no matter what direction the flow of control is going in.

Kỹ thuật tương tự được sử dụng để vượt qua tất cả các đường biên trong kiến trúc. Chúng ta lợi dụng đa hình động để tạo ra những mối phụ thuộc mã nguồn ngược hướng với luồng điều khiển, nhờ đó tuân thủ Nguyên tắc Phụ thuộc.

What data crosses the boundaries

Hàng vượt biên

Typically the data that crosses the boundaries is simple data structures. You can use basic structs or simple Data Transfer objects if you like. Or the data can simply be arguments in function calls. Or you can pack it into a hashmap, or construct it into an object. The important thing is that isolated, simple, data structures are passed across the boundaries. We don’t want to cheat and pass Entities or Database rows. We don’t want the data structures to have any kind of dependency that violates The Dependency Rule.

Thông thường dữ liệu vượt biên có cấu trúc không phức tạp. Bạn có thể sử dụng các cấu trúc đối tượng cơ bản, như Data Transfer chằng hạn. Hoặc đơn giản hơn nữa thì là một bộ đối số của lời gọi hàm. Bạn có thể gói dữ liệu vào một hashmap, hoặc định hình chúng thành một đối tượng. Quan trọng nhất với hàng vượt biên là chúng phải cô lập và đơn giản. Chúng ta sẽ không muốn cho các thực thể, hay các đối tượn Database row được vượt biên. Chúng ta không muốn vi phạm Quy tắc Phụ thuộc khi đưa dữ liệu vượt biên.

For example, many database frameworks return a convenient data format in response to a query. We might call this a RowStructure. We don’t want to pass that row structure inwards across a boundary. That would violate The Dependency Rule because it would force an inner circle to know something about an outer circle.

Lấy ví dụ, nhiều database framework trả về một dữ liệu rất dễ dùng để làm kết quả cho một câu truy vấn. Tên của cấu trúc dữ liệu là RowStructure chẳng hạn. Chúng ta sẽ không muốn đưa đối tượng row đó vào những vòng trong. Như thế sẽ buộc vòng trong phải biết đến một chi tiết của vòng ngoài. Như thế sẽ vi phạm Quy tắc Phụ thuộc.

So when we pass data across a boundary, it is always in the form that is most convenient for the inner circle.

Cho nên dữ liệu vượt biên luôn có định dạng thuận tiện nhất cho vòng trong.

Conclusion

Lời kết

Conforming to these simple rules is not hard, and will save you a lot of headaches going forward. By separating the software into layers, and conforming to The Dependency Rule, you will create a system that is intrinsically testable, with all the benefits that implies. When any of the external parts of the system become obsolete, like the database, or the web framework, you can replace those obsolete elements with a minimum of fuss.

Những quy tắc trên đơn giản, không khó thích ứng, và sẽ giúp bạn bớt đi rất nhiều đau đầu về sau. Bằng cách phân tách phần mềm thành các tầng, và tuân thủ Quy tắc Phụ thuộc, hệ thống sẽ có khả năng kiểm thử được một cách tự nhiên, và bạn sẽ nhận được rất nhiều lợi ích theo đó. Những thành phần của hệ thống, chằng hạn như database hay web framework có thể thay thế được theo một cách ít phiền toái nhất.

_

Leave a Comment

Your email address will not be published. Required fields are marked *