Nguyen Le PhongNguyen Le Phong

Software Architecture FoundationsPhần 13/13

Layered và Vertical Slice Architecture

Layered architecture tạo cảm giác gọn gàng lúc ban đầu, còn vertical slice giữ thay đổi gần với feature mà người dùng thật sự chạm vào. Một bài nhìn chậm lại về hai cách tổ chức này, mỗi cách giúp được gì, và cách team có thể đi về boundary rõ hơn mà không biến kiến trúc thành nghi thức.

Buổi sáng thường bắt đầu rất vô hại. Ai đó mở một pull request để thêm một rule nhỏ cho payment, nhưng danh sách file dài hơn cảm giác của thay đổi. Một controller, một service, một repository, một mapper, một validator, một file test ở thư mục khác, và một helper shared mà không ai chắc nó còn thuộc về ai. Rule thì đơn giản. Đường đi để đặt nó vào codebase thì không.

Đây là lúc kiến trúc rời khỏi sơ đồ và trở thành cảm giác hằng ngày. Layered architecture có thể nhìn rất gọn từ bên ngoài. Controller ở với controller. Service ở với service. Repository ở với repository. Nó dễ dạy, dễ generate, và dễ nhận ra khi team còn mới. Các thư mục gọn vì mỗi thư mục trả lời câu hỏi: đây là loại code gì?

Vertical slice architecture hỏi câu khác: thay đổi nghiệp vụ hoặc trải nghiệm người dùng này thuộc về đâu? Thay vì rải một feature qua nhiều lớp kỹ thuật, nó giữ route, validation, use case, data access và test gần với thứ đang được ship. Một slice có thể tên create-invoice, approve-refund, hoặc change-email-address. Thư mục đọc giống sản phẩm hơn là tutorial framework.

Layered architecture không sai. Với ứng dụng nhỏ, nó có thể rất tử tế. Người mới biết phải nhìn đâu. Quy ước chung rõ ràng. Nếu cả team làm xuyên toàn bộ codebase, nhóm theo trách nhiệm kỹ thuật có thể đủ. Vấn đề xuất hiện chậm, thường sau feature thứ mười hoặc hai mươi. Một thay đổi đáng lẽ cục bộ bắt đầu chạm mọi layer. Reviewer phải nhảy nhiều nơi. Xóa một feature cũ trở thành công việc khảo cổ.

Vertical slice giảm blast radius đó. Nếu luồng approve refund đổi, phần lớn công việc nằm trong slice approve refund. Test ở gần behavior. Use case có model nhỏ của riêng nó mà không giả vờ rằng mọi khái niệm đều phải shared toàn cục. Các layer kỹ thuật vẫn còn, nhưng chúng nằm bên trong feature thay vì là thứ đầu tiên codebase nói ra.

Cái giá là kỷ luật. Slice có thể biến thành nhiều hòn đảo nhỏ nếu team không thống nhất cách đặt tên, utility dùng chung và boundary. Copy một mapper nhỏ hai lần có thể tốt hơn tạo abstraction shared quá sớm, nhưng copy mọi thứ mãi mãi lại thành debt khác. Quy tắc bình tĩnh là: chỉ share khi duplication chứng minh đó là cùng một ý tưởng, không chỉ cùng hình dạng.

Mình thích nhìn lựa chọn này qua nhịp thay đổi. Nếu công việc thường đến dưới dạng capability sản phẩm, vertical slice giúp code đi theo công việc. Nếu công việc thường là cải tiến platform hoặc thay đổi kỹ thuật xuyên suốt, layer vẫn có thể tự nhiên. Nhiều team rơi vào giữa: feature folder ở cấp cao nhất, layer nhỏ bên trong mỗi feature, và một shared folder thật nhàm chán cho thứ đã thật sự chung.

Migration không cần kịch tính. Chọn một feature mới và xây nó như một slice. Giữ public boundary nhỏ. Chỉ di chuyển code thay đổi cùng nhau. Để feature tiếp theo dạy bạn hình dạng đó có giúp không. Kiến trúc tiến bộ trung thực nhất khi nó đi theo bằng chứng lặp lại, không phải một ý kiến mạnh trong cuộc họp.

Điều còn lại là cấu trúc nên rút ngắn khoảng cách giữa ý định và thay đổi. Nếu developer đọc tên thư mục, hiểu hành động nghiệp vụ, sửa được, test được, và để phần còn lại yên, kiến trúc đang làm việc có ích. Nếu mỗi thay đổi nhỏ giống như đi qua mọi tầng của tòa nhà để bật một bóng đèn, có lẽ đã đến lúc thử một slice.

Nếu team bạn từng chuyển từ layer sang slice, hoặc quyết định ở lại với layer có chủ đích, mình muốn nghe tín hiệu nào đã làm lựa chọn đó rõ ràng.

Bạn thấy bài viết thế nào?