Soldemy
0%

SOLID trong Java Spring Boot

Lời mở đầu

9 phút đọc

Lời mở đầu

Một tính năng mới thường bắt đầu bằng một yêu cầu nghe có vẻ nhỏ: thêm một loại khuyến mãi, hỗ trợ thêm một hình thức thanh toán, hoặc thay đổi điều kiện cấp chứng chỉ. Trong một codebase được thiết kế tốt, đội phát triển có thể xác định đúng nơi cần sửa, bổ sung test và triển khai với mức rủi ro có thể kiểm soát. Trong một codebase khác, cùng yêu cầu đó buộc người lập trình lần theo nhiều service, sửa một chuỗi if-else, khởi động cả ứng dụng để kiểm tra một business rule, rồi chờ xem thay đổi có gây regression ở chức năng tưởng như không liên quan hay không.

Khoảng cách giữa hai codebase ấy không nằm ở số lượng annotation, cũng không được giải quyết chỉ bằng việc đặt thêm interface trước mỗi class. Nó nằm ở cách trách nhiệm được phân chia, contract được công bố, dependency được định hướng và boundary được bảo vệ khi hệ thống thay đổi. Đó là khoảng trống mà cuốn sách này muốn giải quyết.

Vì sao có cuốn sách này

SOLID thường được giới thiệu bằng năm định nghĩa ngắn và một vài ví dụ OOP độc lập. Cách trình bày đó hữu ích để ghi nhớ tên nguyên lý, nhưng chưa đủ cho công việc thực tế. Một lập trình viên có thể phát biểu đúng Single Responsibility Principle nhưng vẫn tách một @Service thành quá nhiều class không có boundary rõ ràng. Một đội phát triển có thể tạo interface cho mọi dependency nhưng vẫn để business policy phụ thuộc vào JPA entity, vendor SDK hoặc cấu trúc package của framework. Code nhìn có vẻ tuân thủ SOLID, trong khi chi phí thay đổi gần như không giảm.

Khoảng trống càng rõ khi các nguyên lý được đưa vào Spring Boot. Dependency Injection giúp kết nối object thuận tiện, nhưng một dependency được inject không tự động trở thành dependency đúng hướng. Stereotype annotation giúp phân loại bean, nhưng @Controller, @Service@Repository không tự tạo nên một architecture tốt. Framework có thể khởi tạo một object graph hợp lệ ngay cả khi boundary nghiệp vụ của hệ thống đang sai.

Cuốn sách này ra đời để nối phần lý thuyết thiết kế với những quyết định cụ thể trong một ứng dụng Java Spring Boot: nên tách class ở đâu, khi nào một variation axis đủ ổn định để tạo extension point, điều gì làm nên behavioral contract, interface nên thuộc về client nào, port nên được đặt ở module nào và loại test nào có thể chứng minh thiết kế đang hoạt động như dự định.

Ghi chú

Mục tiêu của sách không phải làm cho code có nhiều abstraction hơn. Mục tiêu là giúp mỗi thay đổi nghiệp vụ đi qua một phạm vi đủ nhỏ, có contract đủ rõ và được kiểm chứng với chi phí hợp lý.

Một cách học SOLID khác

Năm nguyên lý trong sách không được trình bày như năm điều luật độc lập. Chúng được đặt trong cùng một câu hỏi lớn: thiết kế này ảnh hưởng thế nào tới cost of change? Từ câu hỏi đó, người đọc sẽ lần lượt quan sát cohesion của một class, khả năng mở rộng hành vi, tính thay thế của subtype, bề mặt dependency mà một client phải gánh chịu và hướng đi của source dependency giữa policy với detail.

Cách tiếp cận này cũng thay đổi cách đánh giá một bản refactor. Bản after không mặc nhiên tốt hơn vì có nhiều class, nhiều interface hoặc sử dụng một design pattern quen thuộc. Nó chỉ tốt hơn khi có thể chỉ ra pressure mà thiết kế đang xử lý, phạm vi thay đổi được thu hẹp, contract trở nên rõ hơn, test cung cấp confidence tốt hơn và chi phí mới của abstraction là chấp nhận được.

Vì vậy, mỗi nguyên lý đều được trình bày cùng giới hạn áp dụng. Có những đoạn code nên giữ đơn giản. Có những extension point chưa nên xuất hiện vì hệ thống chưa có variation thực. Có những interface nhỏ tới mức làm người đọc phải di chuyển qua quá nhiều file mà không giảm coupling đáng kể. Tính linh hoạt nằm ở quyết định có tạo abstraction hay hierarchy hay không, tạo ở mức nào và vào thời điểm nào. Nhưng khi một public contract đã được công bố và được client sử dụng, correctness của contract không phải phần có thể thỏa hiệp.

Vì sao là Java và Spring Boot

Java cung cấp một type system đủ rõ để phân tích interface, inheritance, composition và dependency ở compile-time. Spring Boot đưa những quyết định ấy vào môi trường gần với công việc backend doanh nghiệp: bean được quản lý bởi container, dependency được kết nối qua constructor, repository tương tác với persistence, controller mở application boundary và test có thể chạy ở nhiều phạm vi khác nhau.

Sự kết hợp này đặc biệt phù hợp để học SOLID một cách thực tế. Người đọc không chỉ xem hai class Java gọi nhau, mà còn thấy một quyết định thiết kế tác động tới component scanning, bean selection, transaction boundary, Spring Data repository, MVC/JPA test slice và khả năng thay thế infrastructure adapter. Đồng thời, sách luôn phân biệt nguyên lý thiết kế với cơ chế framework: Spring hỗ trợ hiện thực một số quyết định, nhưng không đưa ra quyết định thay cho đội phát triển.

Cuốn sách dành cho ai

Cuốn sách hướng tới sinh viên và lập trình viên đã biết Java cùng OOP cơ bản nhưng chưa có nhiều cơ hội làm việc với một codebase Spring Boot phát triển qua thời gian. Nếu bạn hiểu class, interface, inheritance, exception và có thể chạy một project Maven, bạn đã có đủ nền tảng để bắt đầu. Chương 2 sẽ xây phần kiến thức Spring cần thiết cho các chương còn lại, vì vậy kinh nghiệm Spring Boot thực chiến không phải điều kiện bắt buộc.

Lập trình viên đã làm Spring Boot cũng có thể sử dụng sách như một khung review thiết kế. Các phần về behavioral contract, client-specific interface, dependency ownership, contract test và architecture test đặc biệt hữu ích khi đánh giá pull request hoặc cải thiện một module đang chịu áp lực thay đổi. Tuy nhiên, sách không cố thay thế một tài liệu toàn diện về Spring Boot, Java, Domain-Driven Design hay software architecture.

Một hệ thống tiến hóa xuyên suốt chín chương

Thay vì thay domain sau mỗi nguyên lý, cuốn sách theo dõi một nền tảng học trực tuyến với các concern quen thuộc: catalog khóa học, enrollment, học phí, khuyến mãi, thanh toán, tiến độ, nhắc hạn và chứng chỉ. Một số thiết kế xuất hiện ở dạng đơn giản trong chương đầu, bộc lộ giới hạn khi requirement thay đổi, rồi được cải thiện ở các chương sau. Người đọc nhờ đó có thể quan sát SOLID như một chuỗi quyết định liên quan, không phải tập hợp các ví dụ rời rạc.

Mỗi chương kết hợp bốn lớp học tập. Lý thuyết cung cấp định nghĩa và vocabulary chính xác. Ví dụ before/after cho thấy cơ chế refactor trong Java Spring Boot. Test biến nhận định thiết kế thành evidence có thể chạy. Bài tập và quiz buộc người đọc phân tích tình huống mới thay vì chỉ nhận ra ví dụ vừa đọc. Chương cuối cùng đưa các quyết định từ class và interface lên package, module và architecture boundary, rồi hợp nhất chúng trong capstone.

Cách nhận được nhiều giá trị nhất từ sách

Bạn có thể đọc toàn bộ nội dung trên LMS, nhưng không nên học cuốn sách như một chuỗi bài viết chỉ cần đánh dấu hoàn thành. Khi gặp code before, hãy dừng lại trước khi xem bản refactor. Xác định requirement nào đang thay đổi, client nào bị ảnh hưởng, contract nào đang mơ hồ và test nào hiện khó viết. Sau đó hãy tự đề xuất một thay đổi nhỏ nhất có thể giải quyết pressure đang có.

Sau khi đọc bản after, hãy so sánh lập luận chứ không chỉ so sánh cấu trúc file. Chạy project companion, thay đổi một rule, cố ý thêm một implementation và quan sát test nào bảo vệ hành vi. Với bài tập cuối chương, nên làm trước khi mở đáp án; giá trị nằm ở việc giải thích quyết định và trade-off, không chỉ ở kết quả cuối cùng.

Nếu đang áp dụng sách vào một dự án thật, đừng bắt đầu bằng một chiến dịch refactor toàn bộ codebase. Hãy chọn một module đang có change pressure rõ, thiết lập safety net bằng test, thực hiện một thay đổi có phạm vi nhỏ rồi đo lại tác động. SOLID phát huy giá trị khi nó giúp đội phát triển đưa ra quyết định tốt hơn qua nhiều lần thay đổi, không phải khi nó được dùng để chấm điểm hình thức của source code.


Lời hứa với người đọc

Cuốn sách không hứa cung cấp một thiết kế đúng cho mọi hệ thống. Không framework, design pattern hay bộ nguyên lý nào có thể loại bỏ sự cần thiết của judgment. Điều cuốn sách cam kết là cung cấp một cách suy nghĩ có hệ thống: bắt đầu từ evidence, xác định boundary và contract, lựa chọn abstraction tương xứng với rủi ro, kiểm chứng bằng test, rồi biết dừng khi chi phí thiết kế vượt quá giá trị dự kiến.

Sau chín chương, kết quả quan trọng nhất không phải là khả năng đọc thuộc năm chữ cái S-O-L-I-D. Kết quả quan trọng là khi đứng trước một yêu cầu mới hoặc một pull request khó đánh giá, bạn có thể đặt đúng câu hỏi, nhìn thấy dependency thực sự và giải thích vì sao một quyết định thiết kế phù hợp với bối cảnh hiện tại.

Nếu cuốn sách giúp những thay đổi tiếp theo trong codebase của bạn trở nên rõ ràng hơn, có phạm vi nhỏ hơn và được kiểm chứng tốt hơn, nó đã hoàn thành mục đích của mình.

Hỏi đáp

Đăng nhập để đặt câu hỏi và tham gia thảo luận.