Từ Tầm nhìn đến Cỗ máy - Bản thiết kế Thực thi cho CScaf Core - CScaf #2.1
Do giới hạn của Viblo là 70,000 từ mỗi bài nên bài sẽ được chia làm nhiều phần và mỗi ngày sẽ đăng một phần.
Cuộc thảo luận về tương lai của ngành phát triển phần mềm bắt đầu từ đây. Mọi ý kiến của bạn, dù là đồng tình hay thách thức, đều là một phần quan trọng của cuộc đối thoại này. Hãy tham gia bằng cách bình luận hoặc gửi email về contact@axx83.com.
Chào mừng bạn đến với sự khởi đầu của một kỷ nguyên mới.
Tóm tắt
Tài liệu này đánh dấu một bước chuyển mình quan trọng: từ triết lý trừu tượng sang một bản thiết kế có thể thi công. "Tuyên ngôn Lập trình Hướng Kiến trúc" đã trả lời cho câu hỏi "Tại sao?", giờ đây, LDMV này sẽ tập trung trả lời câu hỏi "Như thế nào?". Đây không phải là bản thiết kế cho đích đến cuối cùng, mà là bản thiết kế cho cây cầu đầu tiên, vững chắc nhất để đưa chúng ta đến đó.
Triết lý Lập trình Hướng Kiến trúc (SOP) mà tôi theo đuổi không vốn dĩ gắn liền với bất kỳ ngôn ngữ nào; nó là một mô hình tư duy về cách cấu trúc phần mềm ở quy mô lớn. Về lý thuyết, nó có thể được áp dụng cho Java, TypeScript, hay bất kỳ hệ sinh thái nào khác. Tuy nhiên, để biến lý thuyết thành thực tiễn, tôi đã chọn C# làm nền tảng tiên phong. Với sự trưởng thành của hệ sinh thái .NET, sự mạnh mẽ của trình biên dịch Roslyn, và vị thế là một trong những ngôn ngữ OOP hiện đại hàng đầu, C# chính là phòng thí nghiệm hoàn hảo để thử nghiệm và chứng minh giá trị của những ý tưởng này.
Trọng tâm của tài liệu này là giới thiệu một mô hình phát triển đột phá mà tôi gọi là "meta-build". Đây là một quy trình nơi nhà phát triển được tận hưởng trải nghiệm làm việc trên một codebase thống nhất, mạch lạc như một monolith, nhưng kết quả của quá trình build lại là một hệ thống microservices vật lý, có thể triển khai độc lập. Tôi sẽ đi sâu vào cách công cụ build Wcd hoạt động như một "dịch giả kiến trúc", tự động phân tích ý định của nhà phát triển và biến đổi project CScaf trừu tượng thành nhiều project C# truyền thống. Cuối cùng, chúng ta sẽ khám phá Inator, một runtime phổ quát được nhúng vào mọi Space, chịu trách nhiệm khởi chạy, quản lý và kết nối chúng lại với nhau thành một hệ thống phân tán hoàn chỉnh.
Để làm rõ hơn về hành trình dài phía trước, tôi định hình dự án CScaf qua ba giai đoạn lớn:
- Giai đoạn 1: MVP (Minimum Viable Product): Một phiên bản cực kỳ tinh gọn của bộ thư viện C# Core, được thiết kế chỉ với một mục đích: chứng minh các cơ chế cốt lõi (meta-build và runtime phi tập trung) là khả thi về mặt kỹ thuật.
- Giai đoạn 2: C# Library (Thư viện C# Hoàn chỉnh): Một bộ công cụ mạnh mẽ, ổn định, và giàu tính năng dành cho C#. Đây là phiên bản hoàn thiện của "cây cầu", một sản phẩm giá trị mà các nhà phát triển .NET có thể sử dụng để xây dựng các hệ thống phức tạp trong thực tế.
- Giai đoạn 3: New Language (Ngôn ngữ CScaf): Đích đến cuối cùng. Một ngôn ngữ lập trình mới được xây dựng từ đầu dựa trên các nguyên tắc của SOP, nơi Space, swait, và overspace là những công dân hạng nhất, loại bỏ hoàn toàn các giới hạn và sự đánh đổi của cách tiếp cận dựa trên thư viện.
Tài liệu này chính là bản thiết kế chi tiết cho Giai đoạn 1 và là cơ sở cho giai đoạn 2. Nó là lộ trình thực tiễn để biến những ý tưởng táo bạo nhất của CScaf thành một sản phẩm khả thi, cho phép chúng ta xác thực, thử nghiệm, và xây dựng tương lai của Lập trình Hướng Kiến trúc trên một nền tảng vững chắc.
Phần I: Triết lý Hiện thực hóa - "Từ Ứng Dụng Đơn Lẻ đến Hệ Thống Hữu Cơ"
1, Phá vỡ Cái lồng Cổ xưa: Kết thúc Kỷ nguyên của "Ứng dụng Đơn lẻ"
Để thực sự hiểu được bước nhảy vọt mà CScaf và Lập trình Hướng Kiến trúc (SOP) mang lại, chúng ta phải quay trở lại và thách thức giả định nguyên thủy nhất đã định hình nên ngành phần mềm: khái niệm về một "ứng dụng" (application).
Từ những ngày đầu của mã máy nhị phân, qua kỷ nguyên của Assembly, và cho đến tận đỉnh cao của Lập trình Hướng Đối tượng (OOP) hiện đại, tư duy của chúng ta vẫn bị giam cầm trong một mô hình duy nhất: một chương trình là một thực thể khép kín, tự trị và về cơ bản là đơn chức năng. Dù nó là một công cụ dòng lệnh đơn giản hay một hệ thống quản lý doanh nghiệp phức tạp, nó vẫn được quan niệm, được thiết kế, và được build như một "hộp đen" duy nhất, chạy trên một máy tính, trong một không gian địa chỉ, với một mục đích cụ thể.
Microservices, mặc dù trông có vẻ là một sự thay đổi, thực chất chỉ là một giải pháp tình thế. Nó không phá vỡ mô hình này, mà chỉ đơn giản là nhân bản nó lên. Thay vì một ứng dụng khổng lồ, chúng ta tạo ra hàng trăm ứng dụng nhỏ hơn, mỗi cái vẫn là một "hộp đen" khép kín, tự trị và đơn chức năng theo đúng định nghĩa cũ. Chúng ta chỉ đang chuyển sự phức tạp từ bên trong các ứng dụng ra khoảng không gian trống rỗng giữa chúng, một không gian không có luật lệ, không có trình biên dịch kiểm soát, và đầy rẫy những rủi ro về sự không nhất quán.
CScaf không tìm cách cải tiến microservices, và nó cũng không tìm cách quay lại thời của monolith. Nó tìm cách thay đổi cốt lõi của khái niệm lập trình.
Tầm nhìn "Siêu Project": Bản thiết kế cho một Hệ thống Hữu cơ
CScaf đề xuất rằng chúng ta ngừng viết "các ứng dụng". Thay vào đó, chúng ta viết một bản thiết kế tổng thể (master blueprint) cho một hệ thống hữu cơ.
Trong mô hình này, nhà phát triển làm việc trên một và chỉ một "siêu project". Đây không phải là một project theo nghĩa truyền thống. Nó không mô tả một chương trình; nó mô tả một thế giới. Nó là một Nguồn Chân lý Duy nhất (Single Source of Truth - SSOT) với quy mô không giới hạn. Hãy tưởng tượng một project chứa vài tỉ dòng code, nơi toàn bộ hạ tầng số của một gã khổng lồ như Google—từ công cụ tìm kiếm, hệ thống quảng cáo, đến dịch vụ đám mây—đều được định nghĩa và kết nối trong cùng một không gian ngữ nghĩa.
Đây mới là tầm nhìn đích thực của ngôn ngữ CScaf. Trong thế giới đó, sự phân tách chức năng không còn là việc chia cắt codebase thành các hòn đảo cô lập, mà là việc định nghĩa các cơ quan chức năng khác nhau bên trong cùng một cơ thể sống, tất cả đều được quản lý và đảm bảo tính toàn vẹn bởi một trình biên dịch có nhận thức về toàn bộ hệ thống.
Cây cầu Hiện thực: Thêm một Bước, Thay đổi một Tư duy
Tầm nhìn về một "siêu project" có thể cảm thấy xa vời và triệt để. Tuy nhiên, vẻ đẹp của cách tiếp cận mà tôi đề xuất nằm ở sự thực dụng của nó. Trong giai đoạn đầu, Lập trình Hướng Kiến trúc không yêu cầu chúng ta phải vứt bỏ toàn bộ quy trình làm việc hiện tại. Thay vào đó, nó chỉ thêm một bước duy nhất, mạnh mẽ vào phía trước của mô hình truyền thống.
Quy trình phát triển truyền thống là: Viết code trong một project -> Build -> Ra một file thực thi.
Quy trình của CScaf trong giai đoạn thư viện là: Viết code trong siêu project -> (Bước mới) Dịch thành nhiều project truyền thống -> Build từng project đó -> Ra nhiều file thực thi.
Bước mới này, quy trình "meta-build", chính là trái tim của sự đổi mới. Nó được điều phối bởi công cụ Wcd, hoạt động như một "dịch giả kiến trúc". Nó lấy bản thiết kế tổng thể (siêu project) làm đầu vào và tự động tạo ra nhiều project C# đơn lẻ, quen thuộc làm đầu ra. Mỗi project được sinh ra này là một project C# hoàn toàn tiêu chuẩn mà hệ sinh thái .NET có thể hiểu và build một cách bình thường.
Bằng cách thêm vào chỉ một lớp trừu tượng này, chúng ta vừa có thể tận hưởng một tư duy thiết kế hoàn toàn mới ở cấp độ cao, vừa có thể tái sử dụng toàn bộ sức mạnh và sự ổn định của hệ sinh thái công cụ hiện có ở cấp độ thấp.
Biểu hiện Vật lý Tương tự, Bản chất Hoàn toàn Khác biệt
Khi quy trình meta-build hoàn tất, kết quả vật lý trên ổ cứng của bạn—một tập hợp các file thực thi độc lập—trông không khác gì một hệ thống được xây dựng theo kiến trúc microservices truyền thống. Chúng có thể được đóng gói vào container, được triển khai lên cloud, và được mở rộng một cách độc lập. Sự tương đồng này là có chủ đích; nó đảm bảo tính tương thích và dễ hiểu.
Tuy nhiên, trong khi biểu hiện vật lý là tương tự, bản chất của chúng lại hoàn toàn khác biệt.
Các microservices truyền thống giống như những sinh vật độc lập, được sinh ra ở những nơi khác nhau, nói những ngôn ngữ địa phương khác nhau, và phải học cách phối hợp với nhau thông qua các giao thức và hợp đồng được định nghĩa một cách lỏng lẻo.
Ngược lại, các thành phần được sinh ra từ CScaf giống như các cơ quan trong cùng một cơ thể sống. Chúng được sinh ra từ cùng một DNA (siêu project). Chúng chia sẻ một ngôn ngữ chung và một sự hiểu biết bẩm sinh về cấu trúc và chức năng của nhau, bởi vì mối quan hệ đó đã được định nghĩa một cách chặt chẽ trong bản thiết kế gốc.
Sự thay đổi từ việc "phối hợp" giữa các thực thể riêng lẻ sang việc "vận hành" các thành phần của một thể thống nhất chính là bước nhảy vọt về tư duy. Nó cho phép chúng ta quản lý sự phức tạp ở quy mô lớn không phải bằng cách vá víu từ bên ngoài, mà bằng cách áp đặt một trật tự và sự mạch lạc từ bên trong.
2. Mục tiêu của Giai đoạn Thư viện tối thiểu (MVP): Một Phòng thí nghiệm Chiến lược
Một tầm nhìn, dù có vĩ đại đến đâu, cũng sẽ chỉ là ảo ảnh nếu không có một con đường thực tế để hiện thực hóa nó. Phần này sẽ định nghĩa mục tiêu và, quan trọng hơn, những giới hạn có chủ đích của bước đi đầu tiên trên con đường đó: phiên bản Thư viện C# Tối thiểu Khả thi (Minimum Viable Product - MVP).
Cần phải nói rõ ngay từ đầu: MVP này không được tạo ra để làm cho cuộc sống của nhà phát triển dễ dàng hơn ngay lập tức. Trên thực tế, ở một vài khía cạnh, nó sẽ làm cho mọi thứ trở nên khó khăn hơn. Nó là một phòng thí nghiệm, một công cụ để chứng minh một giả thuyết. Sự thành công của nó không được đo bằng sự tiện dụng, mà bằng việc nó có trả lời được câu hỏi cốt lõi hay không: "Liệu tư duy thiết kế hệ thống này có khả thi về mặt kỹ thuật không?"
Mục tiêu hàng đầu của MVP là chứng minh giả thuyết về trải nghiệm phát triển thống nhất: cung cấp lợi ích của monorepo (dễ tái cấu trúc, chia sẻ code) mà không hy sinh lợi ích của microservices (triển khai độc lập, khả năng mở rộng).
Tuy nhiên, bản thân MVP chỉ giả lập được mô hình này chứ chưa thực sự thay đổi bất cứ gì ở cấp độ công cụ cốt lõi. Nó không can thiệp vào trình biên dịch Roslyn hay runtime của .NET. Điều này dẫn đến một nghịch lý không thể tránh khỏi ở giai đoạn đầu:
- Lời hứa: Bạn có thể mở một "siêu project" duy nhất và về mặt lý thuyết, bạn có thể tái cấu trúc một class dùng chung và thấy sự thay đổi được áp dụng trên toàn bộ hệ thống.
- Thực tế khắc nghiệt: Vì IDE hoàn toàn không nhận thức được lớp "meta-build" của Wcd, sẽ chưa có IntelliSense cho các lời gọi chéo Space. Việc "Go to Definition" sẽ dừng lại ở một lời gọi proxy vô hồn. Việc kiểm tra lỗi tĩnh sẽ không tồn tại. Mọi thứ, từ việc đảm bảo gọi đúng phương thức cho đến việc gỡ lỗi một luồng logic xuyên qua nhiều service, tất cả phải được debug bằng cơm.
MVP cố tình chấp nhận sự đánh đổi này. Nó sẽ tạm thời hy sinh sự tiện lợi ở cấp độ vi mô để chứng minh giá trị to lớn của một cấu trúc mạch lạc ở cấp độ vĩ mô.
Mặc dù trải nghiệm viết code sẽ còn thô sơ, MVP phải chứng minh được một cách thuyết phục giá trị của việc tự động hóa kiến trúc. Chức năng cốt lõi và duy nhất của nó ở giai đoạn này là: đọc một siêu project, chia nhỏ nó ra, và build thành nhiều target độc lập.
Ngay cả với chức năng cơ bản này, nó phải chứng minh được rằng:
- Nhà phát triển được giải phóng hoàn toàn khỏi việc phải viết code boilerplate cho giao tiếp liên-process. Không còn HttpClient, không còn định nghĩa DTO thủ công, không còn cấu hình serialization.
- Quy trình build có thể tự động sinh ra các proxy, các entry point, và các cơ chế giao tiếp một cách đáng tin cậy.
Việc chứng kiến một codebase logic duy nhất, chỉ bằng một lệnh build, có thể "nở" ra thành một hệ thống phân tán đang chạy chính là bằng chứng mạnh mẽ nhất cho tiềm năng của CScaf. Đó là một khoảnh khắc "aha!" mà MVP phải mang lại.
Cuối cùng, và quan trọng nhất, cần phải làm rõ rằng phiên bản thư viện C# (MVP) không phải là đích đến cuối cùng. Nó là một Proof-of-Concept (POC) về mặt tư duy hệ thống và không có nhiều giá trị khi áp dụng vào việc phát triển các ứng dụng thực tế ở giai đoạn này.
Nó không phải là một ngôi nhà hoàn chỉnh để dọn vào ở. Nó là bộ móng và những cây cột thép đầu tiên được đổ bê tông.
Mục tiêu của nó là tạo ra một nền tảng hữu hình, dù không hoàn hảo, để:
- Xác thực các ý tưởng cốt lõi: Biến những gì được viết trong các tài liệu thiết kế này thành một thứ có thể chạy, có thể kiểm chứng, và có thể bị phá vỡ.
- Tạo ra một điểm khởi đầu: Cung cấp một codebase thực tế để từ đó phát triển lên phiên bản Thư viện C# hoàn chỉnh (Giai đoạn 2) và các module trong tương lai như IsABell (thứ sẽ giải quyết vấn đề IntelliSense) hay Busted.
- Xây dựng cộng đồng: Thu hút những người có cùng chí hướng tham gia vào một dự án có thật, không chỉ là một ý tưởng, để cùng nhau định hình và xây dựng tương lai của nó.
Tóm lại, MVP là một bước đi có chủ đích, chấp nhận những khó khăn trước mắt để đổi lấy sự xác thực cho một tầm nhìn dài hạn. Nó là một lời tuyên bố rằng: con đường này gian nan, nhưng nó hoàn toàn khả thi.
Phần II: Giải phẫu Hệ sinh thái CScaf - Tầm nhìn và Hiện thực
Mọi hệ thống phức tạp đều được xây dựng từ những thành phần đơn giản. Hệ sinh thái CScaf, dù tham vọng, cũng không phải là ngoại lệ. Phần này sẽ đi sâu vào giải phẫu của ba thành phần cốt lõi tạo nên Giai đoạn 1 của dự án: Platypus
, Wcd
, và Inator
. Chúng ta sẽ phân tích vai trò, cấu trúc, và sự tương tác của chúng để hiểu cách chúng cùng nhau biến một "siêu project" trừu tượng thành một hệ thống phân tán đang hoạt động.
1. CScaf.Platypus: Thư viện Đánh dấu (The Marker Library)
Thành phần đầu tiên trong bất kỳ hệ sinh thái nào cũng là một lời tuyên bố về triết lý. Với CScaf, thành phần đó là CScaf.Platypus
một thư viện nền tảng nhưng lại nghịch lý một cách có chủ đích. Bản chất của nó, cũng như lý do đằng sau cái tên kỳ lạ này, được gói gọn trong một câu thần chú đơn giản mà tôi thường tự nhủ.
"Nó là một thư viện thú mỏ vịt, nó không làm gì nhiều."
Triết lý đằng sau CScaf.Platypus
có thể được tóm gọn trong một câu nói đơn giản: "Nó là một thư viện thú mỏ vịt, nó không làm gì nhiều."
Thoạt nghe, đây có vẻ là một lời tự hạ thấp, nhưng thực chất nó lại là một tuyên bố mạnh mẽ về thiết kế. Hãy nghĩ về một con thú mỏ vịt trong tự nhiên: nó là một sự tồn tại kỳ lạ, một sự kết hợp của nhiều thứ, nhưng về bản chất, nó chỉ đơn giản là tồn tại. Nó không chủ động xây dựng đập như hải ly hay dệt mạng nhện phức tạp.
CScaf.Platypus
cũng vậy. Đây là một thư viện hoàn toàn thụ động. Nó không chứa một dòng logic thực thi nào. Không có thuật toán, không có vòng lặp, không có lời gọi mạng. Nếu bạn biên dịch nó và nhìn vào bên trong, bạn sẽ chỉ thấy những bộ xương định nghĩa trống rỗng. Nó không làm bất cứ điều gì. Sự tồn tại của nó, giống như con thú mỏ vịt, là để được các thực thể khác trong hệ sinh thái quan sát và nhận diện.
Triết lý này tạo ra một sự tách biệt vai trò cực kỳ sạch sẽ. Platypus
định nghĩa Ý ĐỊNH. Wcd
HIỂU ý định đó. Và Inator
THỰC THI nó. Bằng cách giữ cho Platypus
hoàn toàn vô hại và không có hành vi, chúng ta đảm bảo rằng lớp ngữ nghĩa của hệ thống là độc lập, ổn định và không bị ràng buộc vào bất kỳ logic phức tạp nào.
Ngôn ngữ Giao tiếp giữa Con người và Máy móc
Nếu Platypus
không làm gì, vậy vai trò của nó là gì? Vai trò của nó là trở thành bộ từ vựng, là ngôn ngữ mà qua đó, nhà phát triển (con người) có thể giao tiếp ý định kiến trúc của mình cho công cụ build (Wcd
- máy móc).
Hãy tưởng tượng bạn là một kiến trúc sư thiết kế một tòa nhà chọc trời. Bạn không tự mình đi đổ bê tông hay lắp kính. Bạn vẽ một bản thiết kế. Trên bản thiết kế đó, bạn sử dụng các ký hiệu được tiêu chuẩn hóa: một đường kẻ đôi có nghĩa là một bức tường chịu lực, một hình chữ nhật với một đường cong có nghĩa là một cánh cửa. Những ký hiệu này, bản thân chúng, không có sức mạnh vật lý. Nhưng đối với một đội xây dựng đã được đào tạo, chúng là những chỉ dẫn không thể thiếu, chứa đựng toàn bộ ý định của kiến trúc sư.
Platypus
chính là bộ ký hiệu tiêu chuẩn đó. Khi một nhà phát triển viết dòng mã này ở đầu một file:
// File: AuthenticationService.cs
[CScaf.Platypus.Space("Authentication")]
namespace MyCompany.SuperProject.Services
{
public class AuthenticationService
{
// ... implementation ...
}
}
Họ không viết một đoạn code thực thi. Họ đang đặt một ký hiệu lên bản thiết kế. [CScaf.Platypus.Space("Authentication")]
là một lời tuyên bố, một lời nhắn gửi trực tiếp đến Wcd
:
"Này Wcd, tất cả code trong file này thuộc về một đơn vị kiến trúc logic mà ta gọi là 'Authentication'. Khi đến lúc xây dựng thành phố, hãy đảm bảo rằng tất cả những thứ này được tập hợp lại và xây dựng thành một tòa nhà riêng biệt, độc lập."
Toàn bộ quá trình meta-build phức tạp, quá trình tách code, sinh proxy, và biên dịch ra nhiều thực thể, tất cả đều được khởi đầu bởi những "chú thích" đơn giản và mang tính khai báo này. Platypus
cung cấp những viên gạch ngôn ngữ đầu tiên để xây dựng nên một siêu project.
Cấu trúc và Tầm quan trọng trong Giai đoạn MVP
Để thực hiện vai trò trên, cấu trúc của Platypus
được giữ ở mức tối giản đến mức gần như vô nghĩa nếu đứng một mình:
-
Là một thư viện .NET Standard: Điều này đảm bảo khả năng tương thích tối đa. Nó có thể được tham chiếu bởi bất kỳ loại project .NET nào mà không gây ra xung đột hay kéo theo các dependency không cần thiết.
-
Chỉ chứa các định nghĩa
Attribute
class: Nội dung của thư viện chỉ đơn giản là các file C# định nghĩa các lớp kế thừa từSystem.Attribute
.[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = false)] public sealed class SpaceAttribute : Attribute { public string Name { get; } public SpaceAttribute(string name) => Name = name; } // ... và các attribute khác ...
-
Hoàn toàn không chứa logic: Lặp lại một lần nữa, đây là điều cốt lõi. Không có một phương thức nào ngoài các constructor cơ bản của Attribute.
Cấu trúc tối giản này lại có một tầm quan trọng chiến lược trong giai đoạn MVP:
- Sự đơn giản là Điểm khởi đầu: Đây là thành phần dễ dàng nhất để tạo ra trong toàn bộ hệ sinh thái. Bằng cách hoàn thành nó đầu tiên, chúng ta có ngay một "hợp đồng" vững chắc, một "API" cho công cụ build.
- Tách rời tuyệt đối:
Wcd
phụ thuộc vàoPlatypus
để hiểu ý định, nhưngPlatypus
không phụ thuộc vào bất cứ thứ gì. Sự tách rời này là một nguyên tắc thiết kế phần mềm tốt, cho phép chúng ta phát triểnWcd
vàInator
một cách độc lập mà không sợ làm ảnh hưởng đến lớp "ngữ nghĩa" nền tảng. - Tập trung vào Thử thách chính: Bằng cách chốt hạ "bộ từ vựng" trước, MVP có thể tập trung toàn bộ nguồn lực vào việc giải quyết bài toán khó hơn nhiều: xây dựng công cụ
Wcd
có thể đọc và diễn giải bộ từ vựng đó một cách chính xác.
Tóm lại, CScaf.Platypus
là nền móng tĩnh lặng nhưng không thể thiếu của toàn bộ dự án. Nó là bộ chữ cái trước khi chúng ta có thể viết nên một câu chuyện.
Định nghĩa Cấu trúc của Platypus: Bộ Ký hiệu trên Bản thiết kế
Như đã thảo luận, Platypus
không chứa logic, nó chỉ chứa các định nghĩa. Đây chính là những "ký hiệu" mà kiến trúc sư (nhà phát triển) sẽ sử dụng để vẽ nên bản thiết kế tổng thể của hệ thống. Dưới đây là danh sách và giải phẫu chi tiết của từng ký hiệu trong bộ từ vựng ban đầu.
1. SpaceAttribute
Đây là ký hiệu cơ bản và quan trọng nhất, dùng để phân định các "khu vực chức năng" bên trong siêu project.
-
Mục đích: Đánh dấu một khối code (thường là một class trong một file partial) thuộc về một
Space
logic cụ thể. -
Định nghĩa mã giả:
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public sealed class SpaceAttribute : Attribute { public string Name { get; } public SpaceAttribute(string name) => this.Name = name; } ```* **Quy tắc sử dụng:** 1. `SpaceAttribute` được đặt trước một định nghĩa `partial class`. Điều này có nghĩa là toàn bộ code bên trong cặp ngoặc `{}` của file partial đó sẽ thuộc về `Space` được chỉ định. 2. Nếu một class không được đánh dấu bởi bất kỳ `SpaceAttribute` nào, nó sẽ mặc định thuộc về một `Space` đặc biệt có tên là **`"base"`**. 3. **Quy tắc `base`:** Code thuộc về `Space` "base" là code dùng chung. `Wcd` sẽ đảm bảo rằng code này tồn tại và có thể truy cập được từ **mọi `Space` khác**. Đây là nơi chứa các model, interface, hoặc các tiện ích cốt lõi của toàn bộ hệ thống.
-
Ví dụ:
// File: User.cs (Không có attribute -> thuộc space "base") public class User { public long Id { get; set; } } // --- // File: AuthenticationService.Auth.cs [Space("Authentication")] public partial class AuthenticationService { // Code này chỉ tồn tại trong Space "Authentication". // Nó có thể truy cập class User vì User thuộc "base". public bool Login(User user, string password) { /* ... */ } } // --- // File: AuthenticationService.Billing.cs [Space("Billing")] public partial class AuthenticationService { // Code này chỉ tồn tại trong Space "Billing". // Đây là một ví dụ về một class logic trải dài qua nhiều space, // nhưng mỗi phần lại có chức năng hoàn toàn khác nhau. public void ProcessPaymentFor(User user) { /* ... */ } }
2. OverspaceAttribute
và Cuộc tranh luận về Cấu trúc Code
Overspace
là một khái niệm phức tạp hơn, được sinh ra để giải quyết một vấn đề nan giải: làm thế nào để cùng một khái niệm logic (ví dụ: một Buffer
dữ liệu) có thể có những biểu diễn vật lý hoàn toàn khác nhau trong các Space
khác nhau (ví dụ: một List<byte>
an toàn trong Space
Managed và một byte*
hiệu năng cao trong Space
Unmanaged)?
Trong giai đoạn MVP, vì chúng ta không thể can thiệp vào Roslyn hay CLR, tôi đề xuất hai cách tiếp cận thử nghiệm để mô phỏng Overspace
. Cả hai đều có những ưu và nhược điểm riêng, và mục tiêu của MVP là để xem cách nào tỏ ra dễ đọc và dễ bảo trì hơn trong thực tế.
- Cách 1: Sử dụng
partial
vàSpaceAttribute
(Đã thấy ở trên)- Ý tưởng: Chia một class thành nhiều file partial, mỗi file được đánh dấu bằng một
SpaceAttribute
khác nhau. - Ưu điểm: Cú pháp rất tự nhiên và sạch sẽ. Lập trình viên chỉ cần viết code như bình thường.
- Nhược điểm: Khi làm việc trong IDE, vì tất cả các phần partial đều thuộc cùng một class logic, IntelliSense sẽ gợi ý tất cả các biến và phương thức từ mọi
Space
. Điều này tạo ra sự lộn xộn và rất dễ gây nhầm lẫn.
- Ý tưởng: Chia một class thành nhiều file partial, mỗi file được đánh dấu bằng một
- Cách 2: Sử dụng
OverspaceAttribute
và Sub-class Lồng nhau (Thử nghiệm)-
Ý tưởng: Giữ logic của
Space
"base" ở lớp ngoài cùng của class. Đối với cácSpace
khác, tạo ra các sub-class lồng nhau có tên trùng với tênSpace
đó và đánh dấu các thành viên bên trong bằngOverspaceAttribute
. -
Định nghĩa mã giả:
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] public sealed class OverspaceAttribute : Attribute { public string SpaceName { get; } public OverspaceAttribute(string spaceName) => this.SpaceName = spaceName; }
-
Ví dụ:
public partial class DataBuffer { // Logic cho space "base" public Guid Id { get; private set; } = Guid.NewGuid(); // Logic cho space "BusinessLogic" được lồng vào đây public class BusinessLogicSpace { [Overspace("BusinessLogic")] private List<byte> _buffer = new List<byte>(); [Overspace("BusinessLogic")] public int Count => _buffer.Count; } // Logic cho space "PhysicsEngine" được lồng vào đây public unsafe class PhysicsEngineSpace { [Overspace("PhysicsEngine")] private byte* _buffer; [Overspace("PhysicsEngine")] public int Count { get; set; } } // Để tiện truy cập, ta có thể tạo các property thế này public BusinessLogicSpace BusinessLogic => new BusinessLogicSpace(); public PhysicsEngineSpace PhysicsEngine => new PhysicsEngineSpace(); }
-
Ưu điểm: Phân tách code rất rõ ràng. IDE sẽ không gợi ý nhầm các thành viên của
Space
khác. Khi muốn truy cập, bạn phải tường minh:myBuffer.BusinessLogic.Count
. -
Nhược điểm: Cấu trúc code trở nên rắc rối và sâu hơn. Việc khai báo khó khăn hơn. Việc phải tạo instance của sub-class để truy cập có thể khá phiền toái và tốn kém hiệu năng một chút.
-
3. SAction
: Mô phỏng Giao tiếp Động
Đây không phải là một marker đơn thuần, mà là một static class chứa các phương thức "dummy bán hoạt động". Chúng tồn tại để cung cấp một cú pháp gợi nhớ đến swait
/srun
và là điểm bám để Wcd
có thể tìm thấy và viết lại các lời gọi này.
-
Mục đích: Cung cấp một API để thực hiện các lời gọi xuyên
Space
. -
Định nghĩa mã giả:
public static class SAction { /// <summary> /// Gửi một hành động đến một Space khác và chờ kết quả. /// LƯU Ý: Wcd sẽ viết lại hoàn toàn lời gọi này lúc build. /// </summary> public static Task<T> Swait<T>(string targetSpace, Func<Task<T>> action) { // Trong runtime thực tế, phần thân này sẽ không bao giờ được gọi. // Nó chỉ tồn tại để code có thể biên dịch được. throw new InvalidOperationException("This method should have been rewritten by CScaf.Wcd."); } }
-
Quy tắc sử dụng:
- Để gọi một hàm ở
Space
khác và chờ kết quả, sử dụngawait SAction.Swait(...)
. - Để "bắn và quên" (tương đương
srun
), chỉ cần gọiSAction.Swait(...)
mà khôngawait
. - Cảnh báo tối quan trọng: Mọi thứ được viết bên trong lambda
action
sẽ hoàn toàn nằm ngoài tầm kiểm soát của trình biên dịch và IDE. Nhà phát triển phải tự chịu trách nhiệm đảm bảo rằng họ không truy cập các biến cục bộ không thể serialize, hoặc thực hiện các hành động bị cấm khi giao tiếp xuyênSpace
. Giai đoạn MVP không có cách nào để phát hiện các lỗi này một cách tĩnh.
- Để gọi một hàm ở
-
Ví dụ:
// Code đang chạy trong Space "ApiGateway" public async Task<Guid> RegisterNewUser(string username) { var result = await SAction.Swait<Guid>("UserManagement", async () => { // Code bên trong này sẽ được Wcd trích xuất và gửi đến Space "UserManagement". // Giả sử có một class UserService tồn tại trong Space đó. var userService = new UserService(); // Chú ý: biến 'username' từ context bên ngoài sẽ được Wcd // tự động serialize và gửi đi cùng. return await userService.CreateAsync(username); }); // "Bắn và quên" một event _ = SAction.Swait<bool>("Logging", async () => { Console.WriteLine($"User {username} registered."); return true; }); return result; }
Gánh nặng Nhận thức: Cái giá phải trả của Giai đoạn MVP
Việc định nghĩa các cấu trúc trên cho thấy một sự thật không thể chối cãi: việc sử dụng bộ thư viện này trong giai đoạn MVP sẽ đặt một gánh nặng nhận thức khổng lồ lên vai nhà phát triển.
Sự thật này không phải là một thiếu sót ngẫu nhiên, mà là một hệ quả tất yếu của quyết định táo bạo nhất đã được đặt ra từ đầu: từ bỏ nền tảng tư duy "ứng dụng đơn lẻ" đã tồn tại hàng thập kỷ. Khi nền móng đã bị lật tung, mọi thứ được xây dựng trên nó—từ cách một chương trình khởi động, đến cách nó được build, và cách các thành phần giao tiếp—đều phải được xem xét và định nghĩa lại. Vì vậy, ngay cả ở giai đoạn MVP này, người dùng cũng sẽ cảm nhận được sự thay đổi triệt để này ở mọi ngóc ngách. Nó không chỉ là học một thư viện mới; nó là học một cách suy nghĩ mới.
Hãy tưởng tượng bạn không chỉ đang lái một chiếc máy bay hiệu suất cao với bảng điều khiển bị tắt. Hãy tưởng tượng bạn đang lái một loại phương tiện hoàn toàn mới, và những dụng cụ của chiếc máy bay cũ đơn giản là không còn phù hợp nữa. Bạn có thể lái được, nhưng bạn phải dựa hoàn toàn vào cảm giác, kinh nghiệm, và việc nhìn ra ngoài cửa sổ. Mọi sai lầm đều có thể dẫn đến thảm họa.
Đây chính là trạng thái của việc lập trình với CScaf MVP:
- Không có An toàn Tĩnh: IDE sẽ không báo lỗi nếu bạn cố gắng truyền một đối tượng không thể serialize vào một
Swait
. Nó cũng sẽ không báo lỗi nếu bạn gọi một phương thức không tồn tại trongSpace
đích. Bảng điều khiển cũ không có đèn báo cho những nguy hiểm mới này. - Debug bằng Niềm tin: Khi có lỗi xảy ra ở runtime, việc truy vết một lời gọi từ
Space
này sangSpace
khác sẽ là một quá trình thủ công và đau đớn. Bản đồ cũ không còn hiển thị những con đường mới mà bạn đang đi. - IntelliSense Vô dụng: Như đã nói, IDE sẽ không thể gợi ý hay tự động hoàn thành code bên trong các lambda của
Swait
một cách chính xác. Người phụ lái AI của bạn không được đào tạo để hiểu loại phương tiện này.
Đây là cái giá phải trả để có thể thử nghiệm một mô hình mới mà không cần xây dựng lại toàn bộ hệ sinh thái. Tuy nhiên, đây chỉ là một tình trạng tạm thời. Con đường phía trước đã được vạch ra, và nó chính là quá trình chế tạo ra bộ dụng cụ hoàn toàn mới cho loại phương tiện này:
- Giai đoạn Thư viện Hoàn chỉnh: Sự ra đời của
CScaf.IsABell
(một Roslyn Analyzer) sẽ giống như việc lắp đặt những cảm biến và đèn báo đầu tiên. Nó sẽ cung cấp các cảnh báo lỗi tĩnh ngay trong IDE, giảm đáng kể gánh nặng cho nhà phát triển và mang lại một mức độ an toàn cơ bản. - Giai đoạn Ngôn ngữ Mới: Đích đến cuối cùng, một ngôn ngữ CScaf hoàn chỉnh với trình biên dịch và IDE được xây dựng riêng, sẽ biến trải nghiệm này thành tiêu chuẩn. Bảng điều khiển sẽ không chỉ hoạt động, mà còn là một buồng lái kính (glass cockpit) hiện đại, được thiết kế riêng cho loại phương tiện này, cung cấp cho phi công (nhà phát triển) mọi thông tin họ cần một cách trực quan và an toàn.
2. CScaf.Wcd (What Cha Doin'?): Công cụ Dịch Kiến trúc (The Architecture Translator)
Nếu Platypus
là bản thiết kế tĩnh lặng, là bộ ký hiệu vô hồn trên giấy, thì CScaf.Wcd
chính là vị tổng công trình sư ồn ào, tò mò, và có phần hỗn loạn, người có nhiệm vụ đọc bản thiết kế đó và biến nó thành một công trường xây dựng có tổ chức. Nó là động cơ, là bộ não, là trái tim đang đập của toàn bộ quá trình meta-build. Không có nó, những ký hiệu của Platypus
sẽ mãi mãi chỉ là những dòng Attribute
vô nghĩa.
"Bạn đang định làm cái quái gì ở đây?"
Cái tên "What Cha Doin'?" không chỉ là một trò đùa. Nó chính là triết lý hoạt động, là câu hỏi cốt lõi mà Wcd
đặt ra khi nó nhìn vào từng dòng code trong siêu project của bạn.
Nếu Platypus
là con thú mỏ vịt trầm lặng, thì Wcd
là người bạn thân tọc mạch của nó, người luôn dí sát mặt vào bản thiết kế và hỏi: "Khoan đã, cái [Space("Billing")]
này... bạn đang định làm cái quái gì ở đây? À, tôi hiểu rồi! Bạn muốn toàn bộ logic thanh toán này phải chạy như một thực thể riêng biệt. Được rồi, để tôi lo."
Vai trò của Wcd
không phải là của một công nhân xây dựng. Nó không trực tiếp sinh ra phần lớn code nghiệp vụ của bạn. Vai trò của nó là một bộ điều phối build (build orchestrator), một dịch giả kiến trúc (architecture translator). Đây là một sự khác biệt cực kỳ quan trọng so với các công cụ như Source Generator truyền thống:
- Source Generator thêm code vào bên trong một project đã có sẵn. Nó giống như một người thợ phụ, giúp bạn viết nhanh hơn một vài đoạn code lặp đi lặp lại.
- Wcd (Build Orchestrator) thì khác. Nó đứng ở một cấp độ cao hơn. Nó không làm việc bên trong project của bạn. Nó lấy toàn bộ project của bạn làm đầu vào và tạo ra nhiều project hoàn toàn mới làm đầu ra. Nó không phải là thợ phụ; nó là vị kiến trúc sư trưởng quyết định xem cần phải xây bao nhiêu tòa nhà và bản vẽ của mỗi tòa nhà trông như thế nào.
Nhiệm vụ chính của nó là làm cầu nối giữa hai thế giới: thế giới trừu tượng của nhà phát triển, nơi toàn bộ hệ thống là một thể thống nhất, và thế giới vật lý của công cụ .NET, nơi mọi thứ phải là một project .csproj
độc lập, rõ ràng. Wcd
là thông dịch viên đứng giữa, đảm bảo rằng ý định kiến trúc của bạn được dịch một cách hoàn hảo thành một cấu trúc mà trình biên dịch dotnet
có thể hiểu và thực thi.
Bộ khung Vận hành: Một Vở kịch Bốn Hồi
Phép màu của Wcd
không xảy ra trong một bước duy nhất. Nó là một quy trình được dàn dựng cẩn thận, giống như một vở kịch có bốn hồi, mỗi hồi có một mục đích và kết quả rõ ràng. Khi bạn chạy lệnh dotnet build
trên siêu project, vở kịch này sẽ tự động diễn ra phía sau hậu trường:
- Hồi I: Đọc Kịch bản (Analysis): Đầu tiên,
Wcd
sẽ đọc toàn bộ "kịch bản" – tức là quét toàn bộ codebase của siêu project. Nó không chỉ tìm cácAttribute
củaPlatypus
. Nó xây dựng một bản đồ phụ thuộc hoàn chỉnh, một mô hình nhận thức về toàn bộ hệ thống của bạn. - Hồi II: Phân vai (Code Splitting & Virtual Project Creation): Dựa trên kịch bản đã đọc,
Wcd
bắt đầu "phân vai" cho các diễn viên. Nó quyết định code nào thuộc vềSpace
nào và tạo ra các "phòng thay đồ" riêng cho từng nhóm diễn viên – tức là các project.csproj
ảo trong thư mục build. - Hồi III: Dựng Sân khấu (Boilerplate Generation): Trước khi các diễn viên lên sân khấu,
Wcd
phải dựng cảnh. Nó tự động sinh ra tất cả những thứ cần thiết để vở kịch có thể diễn ra: các "cánh gà" (proxy class) để các diễn viên ở cácSpace
khác nhau có thể tương tác với nhau, và "lối ra sân khấu" (fileProgram.Main
) cho mỗiSpace
. - Hồi IV: Mở màn (Compilation): Khi mọi thứ đã sẵn sàng,
Wcd
lùi lại, vỗ tay ra hiệu, và để cho đạo diễn chính (dotnet build
) bắt đầu công việc của mình. Nó sẽ ra lệnh cho trình biên dịch build từng project ảo đã được chuẩn bị, biến chúng thành các file thực thi cuối cùng.
Bốn hồi này tạo thành một quy trình liền mạch, biến đổi một bản thiết kế tĩnh thành một tập hợp các thành phần động, sẵn sàng hoạt động. Trong các phần tiếp theo, chúng ta sẽ vén bức màn và xem xét chi tiết từng hành động diễn ra trong mỗi hồi của vở kịch phức tạp nhưng đầy mê hoặc này.
Giải phẫu Quy trình Vận hành của Wcd
Để hiểu cách Wcd
hoạt động, chúng ta sẽ đi qua từng hồi của "vở kịch" build, phân tích các hành động, đầu vào, và kết quả của mỗi giai đoạn.
Hồi I: Đọc Kịch bản & Lập Kế hoạch (Analysis & The Planner)
Hồi đầu tiên diễn ra một cách thầm lặng. Wcd
không vội vàng sinh code hay di chuyển file. Thay vào đó, nó đeo kính vào, và bắt đầu đọc. Nó đọc toàn bộ siêu project, không phải với tư cách là một trình biên dịch, mà với tư cách là một kiến trúc sư trưởng.
Công việc này nhanh chóng bộc lộ một hạn chế cố hữu của các công cụ hiện có: Roslyn, dù mạnh mẽ, được thiết kế để phân tích sâu vào bên trong một project duy nhất. Nó không có khái niệm gốc về một "siêu project" hay mối quan hệ giữa hàng chục project "ảo" chưa hề tồn tại. Cố gắng sử dụng Roslyn một mình để giải quyết bài toán này cũng giống như yêu cầu một chuyên gia ngôn ngữ học phân tích một câu đơn lẻ để hiểu được toàn bộ cốt truyện của một bộ tiểu thuyết.
Vì vậy, Wcd
phải hoạt động ở một tầng trừu tượng cao hơn. Nó triển khai một hệ thống mà tôi gọi là Planner.
Planner là một lớp phân tích nằm bên trên Roslyn. Vai trò của nó không phải là phân tích ngữ nghĩa của từng dòng code, mà là xây dựng một bản đồ kiến trúc tổng thể của cả thành phố. Nó quét qua các file, tìm kiếm các ký hiệu [Space]
và SAction.Swait
, và tạo ra một mô hình dữ liệu trong bộ nhớ. Mô hình này chính là bản vẽ quy hoạch, trả lời các câu hỏi:
- Thành phố này có bao nhiêu khu vực (
Space
)? - Những con đường nào (
file code
) thuộc về khu vực nào? - Có những cây cầu (
SAction.Swait
) nào nối giữa các khu vực? Cây cầu đó bắt đầu từ đâu và kết thúc ở đâu?
Việc xây dựng Planner là một công việc tỉ mỉ, đòi hỏi phải xử lý rất nhiều trường hợp phức tạp, nhưng về mặt khái niệm, nó hoàn toàn dễ hiểu. Bất kỳ một dự án xây dựng lớn nào cũng cần một bản vẽ quy hoạch tổng thể trước khi đặt viên gạch đầu tiên. Planner chính là bản vẽ đó. Chi tiết về thiết kế và triển khai của Planner sẽ được dành cho một tài liệu chuyên sâu trong tương lai.
Hồi II: Phân lô & Xây móng (Code Splitting & Virtual Project Creation)
Khi Planner đã hoàn thành bản vẽ, Hồi II bắt đầu. Đây là giai đoạn "lao động chân tay" của Wcd
. Dựa trên bản đồ kiến trúc từ Planner, nó bắt đầu phân lô và xây móng cho từng tòa nhà.
Giai đoạn này chủ yếu tập trung vào việc sao chép và tổ chức file. Nó không cố gắng giải quyết các phần phức tạp như giao tiếp xuyên Space
. Nó chỉ đơn giản là tạo ra một cấu trúc thư mục sạch sẽ và các project C# tiêu chuẩn:
- Với mỗi
Space
được định nghĩa trong bản đồ của Planner (ví dụ: "Authentication", "Billing"),Wcd
sẽ tạo một thư mục tạm trongobj/wcd/Authentication
,obj/wcd/Billing
, v.v. - Bên trong mỗi thư mục, nó tạo ra một file
.csproj
ảo. File này được cấu hình sẵn để tham chiếu đếnCScaf.Platypus
,CScaf.Inator
, và bất kỳ dependency cần thiết nào khác. - Sau đó, nó duyệt qua danh sách các file code C# mà Planner đã xác định là thuộc về
Space
đó (cùng với tất cả các file thuộcSpace "base"
) và liên kết (link) hoặc sao chép chúng vào thư mục ảo này.
Kết thúc Hồi II, chúng ta có một tập hợp các project C# hoàn chỉnh về mặt cấu trúc. Mỗi project đã chứa toàn bộ code cần thiết để hoạt động độc lập. Tuy nhiên, chúng vẫn chưa thể giao tiếp với nhau. Những "cây cầu" trên bản thiết kế vẫn chỉ là những khoảng trống.
Hồi III: Dựng Giàn giáo & Lắp cầu (Boilerplate Generation & Call Rewriting)
Đây là hồi phức tạp và kỳ diệu nhất, nơi Wcd
thực hiện công việc nặng nhọc nhất về mặt trí tuệ. Nó sẽ quay lại những "khoảng trống" đã được để lại ở Hồi II và bắt đầu xây dựng những cây cầu nối liền các Space
.
Trọng tâm của hồi này là xử lý các lời gọi SAction.Swait
. Với mỗi lời gọi được Planner đánh dấu, Wcd
sẽ thực hiện một loạt các thao tác phẫu thuật mã nguồn:
- Phân tích Thân hàm (Lambda Body Analysis):
Wcd
sử dụng Roslyn để phân tích sâu vào khối code bên trong lambda củaSwait
. - Tạo DTO Tối ưu (Optimized DTO Generation): Nó xác định tất cả các biến được "capture" từ context bên ngoài và tự động sinh ra một class DTO (Data Transfer Object) riêng biệt, được "may đo" hoàn hảo cho chỉ riêng lời gọi này. DTO này chỉ chứa những dữ liệu thực sự cần thiết, không một byte thừa.
- Tạo Endpoint ở Đích (Endpoint Generation): Trong project ảo của
Space
đích,Wcd
sinh ra một "endpoint" – một phương thức hoặc một class handler ẩn. Endpoint này biết cách nhận DTO đã được tạo, giải nén nó, và thực thi khối code gốc từ lambda. - Viết lại Lời gọi ở Nguồn (Callsite Rewriting): Đây là bước cuối cùng và quan trọng nhất.
Wcd
quay trở lại project ảo củaSpace
nguồn và xóa hoàn toàn lời gọiSAction.Swait
ban đầu. Nó thay thế lời gọi đó bằng một đoạn code hoàn toàn mới, thực hiện các việc sau:- Tạo một instance của DTO tối ưu và điền dữ liệu vào đó.
- Gọi vào một phương thức của
Inator
, truyền DTO và tên củaSpace
đích.Inator
sẽ lo phần còn lại: serialization, gửi request qua mạng, và chờ đợi kết quả.
Đây là phần khó nhất của Wcd
, nhưng hoàn toàn có thể thực hiện được. Kết thúc Hồi III, tất cả các project ảo không chỉ chứa code nghiệp vụ, mà còn chứa toàn bộ giàn giáo và cầu nối cần thiết để chúng có thể hoạt động như một hệ thống phân tán.
Hồi IV: Mở màn & Đóng gói (Compilation & Packing)
Vở kịch gần đến hồi kết. Mọi thứ đã được chuẩn bị. Hồi cuối cùng là phần đơn giản và máy móc nhất.
Wcd
chỉ đơn giản là thực hiện một vòng lặp foreach
qua tất cả các project ảo mà nó đã tạo ra trong obj/wcd
. Với mỗi project, nó gọi lệnh dotnet build
.
Công việc còn lại chỉ là chờ đợi trình biên dịch .NET hoàn thành nhiệm vụ của mình. Vì các project là độc lập, quá trình này có thể được thực hiện song song để tăng tốc độ.
Sau khi build xong, kết quả sẽ là một thư mục build
chứa các thư mục con cho mỗi Space
. Mỗi thư mục con là một ứng dụng hoàn chỉnh, chứa file DLL/EXE, runtime Inator
, và sẵn sàng để được đóng gói (packing) theo các quy ước sẽ được thảo luận trong một tài liệu khác, chuẩn bị cho việc triển khai. Vở kịch đã kết thúc, và từ một bản thiết kế duy nhất, cả một thành phố đã sẵn sàng để được thắp sáng.
All Rights Reserved