Nhìn từ ngoài vào, một nền tảng booking trông như một cái catalogue gắn thêm bước thanh toán. Search, liệt kê, chọn, trả tiền. Phần cơ khí của việc chạy chừng chín mươi tích hợp supplier cùng lúc mình đã viết ở chỗ khác; bài này nói về cái nằm dưới đó. Domain này không hành xử như một catalogue, và mọi quyết định kiến trúc đáng bảo vệ đều đi ra từ chỗ đó.
Thứ mình đang bán không nằm trong database của mình
Một đêm phòng không phải một row mình sở hữu. Nó là một lời khẳng định về một cái phòng trong một toà nhà của người khác, bán qua một supplier mà supplier đó cũng đang bán đúng cái phòng ấy cho người khác ngay lúc này. Mình không giữ tồn kho. Mình giữ một ý kiến về tồn kho, hình thành đúng lúc mình hỏi, và nó bắt đầu hỏng ngay từ giây sau đó.
Nên khoảng giữa search và book không phải một bước chuyển màn hình, nó là một cửa sổ mà trong đó thế giới thay đổi. Phòng hết. Rate bị rút. Cùng một lượt search, chạy cách nhau một phút, được phép trả về hai kết quả khác nhau và không kết quả nào là bug. Hệ catalogue nào cũng có vấn đề cache cũ; ở đây cái cũ có tiền dính vào, và câu trả lời không phải "cache ít lại" mà là "coi như câu trả lời sẽ hết hạn, rồi thiết kế cái checkout quanh chuyện đó". Deadline tuyệt đối 30 giây trên search hotel, flight và car là một hệ quả nhìn thấy được: supplier trả lời ở giây thứ 31 đang trả lời về một thế giới đã đi qua.
Một cái giá là lời khẳng định có hạn sử dụng
Đó là lý do bước prebook tồn tại. Giá trong kết quả search là một lời rao; giá ở prebook mới là cái supplier chịu giữ, ngay lúc này, trong một lát. Giữa hai mốc đó rate có thể đổi, biến mất, hoặc quay lại với phần thuế khác đi. Bên flight còn thêm hẳn một bước xác nhận giá và thuế trước khi sinh PNR, cũng vì đúng lý do ấy, chỉ là ở một dòng sản phẩm khác.
Hệ quả thiết kế nói ra thì ngắn mà giữ thì tốn: không chỗ nào được coi giá là một giá trị trần trụi. Nó là một giá trị cộng một nguồn cộng một thời điểm, và thành phần nào chỉ giữ lại vế đầu thì đã lặng lẽ trở thành chỗ tiền chảy ra.
Chính sách huỷ là dữ liệu, không phải một cái cờ
"Miễn phí huỷ" là một ô tick trong bộ lọc và là một lời nói dối trong schema. Vật thể thật nằm ở mức từng rate: một hoặc nhiều mốc, mỗi mốc có hạn và có số tiền, có khi tính theo đêm, có khi theo phần trăm, có khi kèm điều khoản no- show hành xử khác với huỷ. Hai rate trên cùng một phòng, cùng một đêm, cùng một supplier, khác nhau là chuyện thường ngày.
Cấu trúc đó chịu lực chứ không để trang trí. Bộ test booking của tụi mình luôn chọn offer rẻ nhất có miễn phí huỷ, và điều đó chỉ làm được vì chính sách đủ cấu trúc để chọn lọc trên nó. Ép nó thành một boolean ở bất kỳ mắt xích nào là sẽ có người phía sau dựng lại sai — và cách mình biết là một khách bị thu tiền cho một lần huỷ mà màn hình đã nói với họ là miễn phí.
Năm sản phẩm không chịu làm một sản phẩm
Tụi mình chạy năm dòng, và đó không phải năm lớp da trên cùng một vật thể. Một đêm phòng là một khoảng ngày cộng số khách trên một cái phòng. Một chuyến bay là một tập chặng kèm PNR, quy tắc giá và e-ticket sau khi thanh toán. Một tour có ngày khởi hành và một sức chứa, tính giá theo đầu người. Một transfer có hai đầu điểm và một giờ đón, mà giờ đón đó thường bị buộc vào giờ hạ cánh của một chuyến bay nó không kiểm soát. Một xe thuê là một khoảng thuê với điểm nhận và điểm trả có thể khác nhau.
Có thể phủ một contract chung lên tất cả — tụi mình làm thế — nhưng contract đó là thoả thuận về việc hỏi những câu nào, không phải lời khẳng định rằng các câu trả lời cùng một loại vật thể. Flight là chỗ lộ ra rõ nhất: nó cần những bước mà không dòng nào khác cần, vì một cái vé là một thứ khác với một chỗ giữ. Mô hình nào xoá mấy khác biệt đó thì rồi sẽ phải nhét chúng trở lại dưới dạng case đặc biệt, đúng vào những chỗ đáng lẽ phải chung.
Nguồn hàng của mình là các công ty khác
Chín mươi tích hợp là chín mươi vùng hỏng độc lập. Mỗi bên có lịch bảo trì riêng, có quan niệm riêng về thế nào là lỗi, có cuối tuần riêng. Mình không sửa được bug của họ, không deploy được bản vá của họ, và trong chín mươi cái thì lúc nào cũng có một cái đang sai. Độ sẵn sàng thật của mình là của mình nhân với của họ; mình chưa bao giờ tính cái tích đó theo từng supplier nên sẽ không đưa con số nào — nhưng chiều của nó thì không phải bàn.
Không chỉ uptime, tính đúng đắn cũng bị phân mảnh theo. Cùng một toà nhà mang định danh khác nhau ở mỗi supplier, và quan hệ định danh có tính bắc cầu, nên việc tra cứu chạy như một lượt duyệt đồ thị trong khi các row nằm ở bảng quan hệ khoảng 1,9 triệu định danh với chừng 46 cột ID supplier. Không có ai để hỏi cho ra chân lý. Tên với toạ độ chỉ là gợi ý; code của một supplier thứ ba xuất hiện ở cả hai phía mới là bằng chứng. Dựng một việc kinh doanh trên những dữ kiện không thể xác minh từ một nguồn duy nhất là tính chất của domain, không phải lựa chọn kỹ thuật.
Trả lời sai là một sự kiện tài chính
Ở phần lớn hệ thống, hậu quả tệ nhất của một lỗi là render sai. Ở đây hậu quả tệ nhất là một cái booking. Map sai là khách ngủ ở một toà nhà khác. Giá ra thấp không phải lỗi hiển thị, nó là quyết định bán lỗ, được thi hành tự động cho tới khi có người nhận ra. Book trùng là hai lần thu tiền và một cuộc nói chuyện hoàn tiền. Một lệnh huỷ không tới được supplier là một hoá đơn.
Đó là lý do mấy thói quen vận hành nhìn từ ngoài vào có vẻ hoang tưởng: mọi cú gọi provider đều log cả request lẫn response và bắn lại được đúng cái curl đã gửi, mọi lệnh ghi vào dữ liệu production đều phải confirm và in ra nó sắp chạy ở đâu, bằng chứng certification được crawl vào một thư mục riêng cho từng testcase. Không cái nào là diễn cho có. Trong domain này, nút undo là một người cầm điện thoại.
Vì sao domain ép ra kiến trúc
Cộng hết lại thì ra một loạt yêu cầu không ai chọn vì nó đẹp: state phải sống sót qua một lần deploy ngay giữa lúc khách checkout, timeout là quyết định sản phẩm chứ không phải chuyện tinh chỉnh, cô lập để một buổi chiều tệ của một supplier không thành buổi chiều tệ của tất cả, chính sách là dữ liệu có cấu trúc, và một tầng định danh là đồ thị vì thực tế nó là đồ thị.
Cứ dựng cái hiển nhiên — một catalogue, một giỏ hàng, một checkout — thì domain sẽ không nhẹ nhàng chỉnh lại mình. Nó đồng ý với mình đủ cho một buổi demo, rồi gửi hoá đơn. Cái khó ở đây không nấp trong chi tiết. Nó chính là chủ đề.