Phần lớn software project (project) lúc đầu triển khai rất trong sáng và nhẹ nhàng; nhưng khi bước vào giai đoạn thương mại hóa thì có thể project dần dần tồn tại nhiều điều kỳ lạ và thậm chí bad code trong đó.
“Điều kỳ lạ” xuất hiện ở nhiều tầng của project, từ tầng lõi, tầng nghiệp vụ, v.v. bởi lý do vô tình hay hữu ý.
Trong bài viết “Core module - Gồm những loại logic nào” tôi xin chia sẻ về trải nghiệm thực tế trong quá trình cùng xây dựng và duy trì project ở tầng lõi; với một nhận xét như sau:
Core module
ngày càng phình to[3] kéo theo độ phức tạp tăng lên dẫn đến vai trò ngày lu mờ
Cuối bài này tôi cũng nêu ra một vài điểm để chúng ta có thể tham khảo giúp quyết định xem liệu source code nào cần đưa vào core module và source code cần nào đưa vào feature module[2].
Trước hết, core module là gì?
Core module: Là module lõi của một nền tảng(platform) mà cần nó để các feature module trong platform hoạt động tốt.
Core module thường chỉ chứa những source code lõi không nhằm phục vụ cho một feature module nào cụ thể. Hoặc những ràng buộc mang tính chuẩn mực dưới dạng service interface, message type. Bên dưới ví dụ về vị trí core module
và features module
được xây dựng
seatmap // Ví dụ về seatmap project có core module
|---node_modules
|---core
| |---componentA
| |---componentB
| |---shared
|
|---allocated-seat
|
|---allocated-user
| |---shared
|
|---package.json
|---package-lock.json
|---README.md
|---// ...
Ngoài ra có một vài thuật ngữ thường được sử dụng trong phần mềm như sau đôi khi dễ gây nhầm lẫn:
basic
logic: Là logic mà từ đó nó không thể trực tiếp mang lại nghiệp vụ của chương trình và thường nó cần được hoàn thiện ở (lớp) dẫn suất. Thiết kế phần mềm hướng đối tượng thường sử dụng wording nàycommon
logic: Là logic được tạo ra dưới dạng mẫu code (code snippet) nhằm giải quyết một điểm nào đó cụ thể. Ví dụ:signIn/signUp
method là một dạng của common logicutility
logic: Mẫu code nhỏ dưới dạng function, class hay module, với mục đích rất chung, chưa đủ ngữ nghĩa để trở thành một "application" rõ ràng.
Và thường nó gom lại dưới một tên gọi cụ thể; nó được hiện thực dưới dạng static (hoặc singleton tech) và được sử dụng nhiều lần, rộng khắp project.
Ví dụ:isInteger(number)
là một method dùng để kiểm tra đối số truyền vô là số nguyên hay không, được gộp lại dưới tên gọiutils/Number.isInteger
helper
logic: Là một tên gọi khác của utility logic, và wording này thường được sử dụng bởi CodeIgniter PHP framework
Đặc trưng của core module trong platform cụ thể
-
Đã built-in sẵn trong platform để sử dụng ở những feature module mà không cần phải cài đặt thêm trước khi sử dụng.
Hay nói cách khác: Nếu sử dụng platform là đã có sẵn core module dưới dạng built-in module và không thể gỡ bỏ nó.
Ví dụ: TrongNodejs platform
. Core module được built-in sẵn và khi feature module có nhu cầu xài chúng ta thoải mái sử dụng mà không cần phải làm thủ tụcinstall
chỉ cầnimport xxx from 'yyy'
để sử dụng . Lúc này ví dụ nhưpath
module thuộcnon-global core module
const path = require('path');// ES6 import path from 'path'; const filename = path.basename(`${__dirname}/demo_path.js`); console.log(filename);
-
Tốn chi phí cao và được xây dựng cho một platform cụ thể thường được đóng góp bởi những tập đoàn, công ty đủ lớn về tầm ảnh hưởng lẫn nhân sự
Chẳng hạn:Nodejs
core được xây dựng và release một lượng lớn components cho core module -
Mục đích chung chung có nghĩa là không phục vụ cho một feature cụ thể.
Dùng node làm nền tảng cho xây dựng ứng dụng mobile view thông qua react native hoặc dùng làm web application và thậm chí native app -
Độc lập hoàn toàn, có nghĩa là Không phụ thuộc vào feature module
Khi feature on/off thì core module không ảnh hưởng và các module còn lại phần lớn hoạt động tốt. -
Bộ automated unit testing đủ
Ba giai đoạn thường gặp của core module
Project triển khai thì nhiều kỹ thuật, công nghệ, kiến trúc, v.v. phù hợp được áp dụng. Những điểm đó thường được lựa chọn cẩn thận và nghiêm túc trước khi dự án bắt đầu.
Theo tôi, dưới góc độ là một developer, dự án thường trải qua ba giai đoạn như sau:
- Giai đoạn xây mới → 2. Giai đoạn thương mại hóa → 3. Giai đoạn Maintenance . Ba giai đoạn này cũng là điều mà phần lớn project owner mong đợi về project của mình. Vì đi được tới 3 giai đoạn này là project sống ngược lại là project đó đã biến mất.
Project được cấu thành từ nhiều modules, như vậy core module cũng trải qua ba giai đoạn thông thường này. Bên dưới tôi chia sẻ thêm về từng giai đoạn của core module
Core module - Giai đoạn xây mới
Giai đoạn đầu xây mới, từ software blueprint đến implementation chỉ trong nháy mắt chúng ta có thể biết được từ vai trò đến mối quan hệ từng thành phần cấu thành nên project. Hình 1.0 bên dưới mô tả core module nhỏ và đơn nhiệm
Hình 1.0: Mối quan hệ giữa core module và feature module khi xây dựng ban đầu
Đến đây chúng ta có thể nhận ra một số đặc trưng của core module lúc này như sau
- Nhỏ, chỉn chu và đơn nhiệm: chỉ có nhiệm vụ cung cấp các dịch vụ thiết yếu không đủ để tạo nên nghiệp vụ (business)
Với vai trò chỉ cung cấp những nhu cầu, tính năng lỏi trong platform
- Đầu tư bài bản và review nghiêm ngặt trước khi đưa vào codebase
từ thiết kế prototype đến diagram
- Đáp ứng nhiều nguyên lý lập trình
S.O.L.I.D là một bộ nguyên lý thường được các lập trình viên truyền tai và áp dụng triệt để
Core module - Giai đoạn thương mại hóa
Source code đã đóng gói và sản phẩm phần mềm đến với khách hàng. Nó đã được kiểm thử nghiêm ngặt từ tính năng, đến bảo mật lẫn hiệu suất ở công ty với nhiều chỉ số cần đáp ứng. Vâng đó là bước tiên quyết của một ứng dụng tin cậy.
Hình 2.0: Mối quan hệ giữa core module và feature module một thời gian sau
Nhưng khi đến tay khách hàng, với một kỹ năng lẫn ngữ cảnh thực tế khác biệt, đôi khi nghiệt ngã hơn nhiều so với môi trường kiểm thử. Lúc này ứng dụng sẽ bộc lộ vài điểm cần thay đổi để đáp ứng tốt hơn “thượng đế” của mình.
Từ những điều trên source code bắt đầu có những thao tác và quyết định để đối ứng cho:
- Refactor để project đáp ứng performance;
- Thay đổi vendor cho một service nào đó so với ban đầu đã triển khai
- Và add new feature cho phù hợp với nhu cầu của khách hàng hoặc chiến lược kinh doanh công ty
- Và cả bug fixed, v.v.
Ngoài ra do sự thay đổi về nhân sự (Developer) dẫn đến
Tài liệu hướng dẫn viết mã và những tiêu chuẩn cho source code project đôi khi không được áp dụng triệt để khi developer mới vào project. Hoặc áp lực của deadline hoặc là source code tại thời điểm đó như vậy là phù hợp với nghiệp vụ. v.v. Và cả hàng triệu lý do hợp lý khác để core-source code lúc này:
- Source code core to dần lên và xuất hiện các góc khuất
- Mối quan hệ đối tượng (composition, aggregation, association) trở nên lộn xộn và manh mún nhỏ lẻ
- Và features module có xu hướng nhỏ lại
Core module - Giai đoạn maintenance
Hình 3.0: Core module lúc này có phần báo động đỏ tăng lên, feature mất màu
1. Core module phình to và trở nên cồng kềnh
Lúc này core module phình to, nếu xét trên dòng code có thể là nhiều nhất trong các modules ở platform. Quá nhiều góc khuất trong project. Và hầu như nó khó (Có thể nói là không thể) mở rộng thêm được nữa.
2. Sự phụ thuộc (dependencies) trở phức tạp
Với dấu hiệu nhận thấy là mối quan hệ giữa các module trở nên “Chặt chẽ” mà mất đi sự lỏng lẻo như ban đầu (Điều này rất nguy hiểm cho project nếu muốn đổi tech hoặc thay đổi nhà cung cấp dịch vụ vendor trong tương lai). Dễ nhận thấy nhất là một module-A sống được cần có module-B (B mất thì A sẽ mất theo)
3. Các feature module dần “mất màu”
Lúc này feature module có nhiều dấu hiệu tương đồng với core module.
Đến một lúc nào đó feature module mà trong đó các cấu thành được dịch chuyển ra khỏi nơi thiết kế ban đầu theo hướng về core module. Feature module trở nên nhỏ dần và hầu như mất tính độc lập (isolation)
4. Trật tự lộn xộn, rối rắm
Có module từ từ tiến dần về phía core module và bị core module bao phủ tất cả những nghiệp vụ của feature module này
Core module - Thực hiện “clean” định kỳ
Giống như nhà chúng ta vậy sạch sẽ nhưng sau một thời gian chúng ta thực hiện lại công việc dọn dẹp để giữ cho sự thoải mái và tiện ích được duy trì.
Về project - core module cũng vậy cần thường xuyên để mắt tới nó và dọn dẹp. Nhiều lý do khách quan lẫn chủ quan nhưng core module càng này càng lộn xộn và có nhiều thứ trở nên không còn phù hợp hoặc không nhu cầu sử dụng.
Nên tùy vào sự đầu tư của mỗi đội nhóm mà chúng ta có một tần suất làm sạch core module. Chẳng hạn 1 tháng 1 lần tất cả developers trong project không implement feature mới, mà tập trung tìm kiếm và liệt kê ra những điểm “khác thường” và tạo ra nhưng PBI sau đó lên kế hoạch để giải quyết nó.
Thời điểm clean có thể có những dịch chuyển source code từ core module về feature module; feature module về core module hoặc thậm chí là nhiều source code bị xóa đi để outsource nó cho một third-party đảm nhiệm
Core module sạch sẽ và “thoáng” Điều này giúp nó luôn luôn powerful và hạn chế góc khuất từ đó giúp mang lại nhiều tiện ích, giá trị hơn cho feature module. Chắc chắn sẽ kéo theo hiệu ứng tích cực domino: giúp project tổng thể ít hidden bugs, cost thấp (lợi nhuận cao hơn) và thân thiện hơn đối với new lẫn old developer (giúp thu hút nhân tài) và gia tăng niềm tin hơn với khách hàng sử dụng sản phẩm.
Bên dưới là đồ mô tả giữ project luôn sạch sẽ như ban đầu
Hình 4.0: Mối quan hệ giữa core module và feature module sau “clean”
Checklist - người gác cửa core module
Đúng như tên gọi của nó, core module là module nền tảng, là module lõi từ đó giúp cho những feature module được hoạt động trôi chảy. Core module lỗi sẽ dẫn đến sự sụp đổ hoàn toàn (lock down) hệ thống trên platform đó.
Vì sự quan trọng và dễ bị suy yếu nên chúng ta nên có một bộ tiêu chí để mỗi developer; khi triển khai, review source code có thể tham chiếu vào nó.
Còn nếu dấu hiệu nào để quyết định “bãi đáp” cho một component hay logic ở core module hay feature module sẽ là tranh cãi nếu không có dấu hiệu rõ ràng. Còn theo tôi là: Bản thân source code trong core module thường thoả mãn các điểm bên dưới:
- Không hoàn thiện về nghiệp vụ mà phần lớn chứa đựng khuôn mẫu.
Quy định service interface và message type để có thể outsource service đến third party
Ví dụ:ScheduleDataBaseService
có hành vigenerateRegularAppointment()
- Không được sửa source ở core module vì những lý do không đến từ chính nó (được yêu cầu từ feature module). Nó có một lý do để tồn tại và cũng một lý do để sửa
- Không bị VCS (version control system) báo khi feature module grows.
Để làm được điều này thường tuân thủ Open-closed principle
Kết bài
Core module trong project cũng như trái tim của một cơ thể con người vậy! Nhỏ nhất so với các phần còn lại và nó nằm sâu thẳm nhưng nó duy trì sự sống và tạo ra giá trị cốt lõi cũng như không thể thiếu của project.
Việc đầu tư và chăm sóc định kỳ core-module giúp project trở nên: tin cậy, hiệu quả và project có thể MAINTAINABLE với thế hệ lập trình tiếp theo.
Source code bất hợp lý đôi khi xuất hiện trong core-module cũng không phải là điều gì đó kinh khủng hoặc dở tệ. Mà đôi khi nó đến như một sự thật hiển nhiên (tất nhiên càng ít càng tốt :)) Điều quan trọng là developer nên open mindset là: “mặc dù tốn cost để định kỳ dọn dẹp và refactor core-module nhưng nó là cần thiết để trái tim project luôn đập nhịp nhàng”. Chắc chắn rằng, để “dọn dẹp” cần nỗ lực từ tất cả người tham gia vào project, với một tần suất định kỳ dựa trên checklist đã thống nhất.
Nội dung bài viết chỉ là góc nhìn từ sự trải nghiệm của cá nhân và nó có thể không phù hợp với nhiều người xin lượng thứ nếu có.