Skip to content

PRD: Bố trí bàn & sơ đồ sàn

ModuleCommerce (CORE-03)PRD IDPRD-FLR-001
StatusShippedOwnerCommerce squad
Date2026-06-15Versionv1.0
Packages@nx/commerce · @nx/sale · @nx/core · apps/sale-rendererURDFLR

TL;DR

Cho một merchant F&B một sơ đồ sàn hoạt động và trạng thái chiếm bàn trực tiếp. Chủ quán thiết kế sàn một lần - một layout có tên chứa một cây zone (sàn → phòng → bàn, sâu tối đa hai cấp) với các zone lá mang unit (bàn) có sức chứa, vị trí trên canvas và style - và dựng hoặc sửa cả cây trong một thao tác aggregate nguyên tử. Tại POS, mở một đơn dine-in sẽ chiếm dụng một hoặc nhiều unit; sơ đồ sàn hiển thị mỗi unit là trống hay bận theo thời gian thực, nhân viên có thể tìm bàn trống, chuyển một nhóm khách từ vùng này sang vùng khác, và bàn được giải phóng khi usage của nó hoàn thành. Mỗi thay đổi chiếm dụng phát một sự kiện trực tiếp để mọi máy đồng bộ, và mỗi đơn hàng giữ một snapshot của unit và đường dẫn zone của nó.

1. Context & Problem

Onboarding dựng lên một tổ chức và các merchant của nó (PRD-ORG-001), nhưng một nhà hàng phục vụ tại bàn vận hành mỗi ngày dựa trên thứ mà onboarding không cung cấp: một bản đồ phòng và ai đang ngồi ở đâu. Thiếu nó, phục vụ không thể nhìn ngay những bàn nào còn trống, bếp không có điểm neo cho một đơn dine-in, và "chuyển bàn 4 ra sân vườn" là một lời dặn miệng không được ghi lại.

Hai công việc tách biệt ẩn sau "bàn". Một là thiết kế - bố trí không gian vật lý (vùng, bàn, ghế, vị trí mỗi thứ trên màn hình) - một tác vụ back-office, ít làm, thuộc về catalogue của merchant. Hai là chiếm dụng - đơn hàng đang sống nằm ở bàn nào, ngay lúc này - một mối quan tâm POS thời gian thực, tần suất cao. Mô hình hóa cả hai thành một khối có thể sửa sẽ khiến bản đồ sàn bị xáo trộn mỗi lần xếp khách, còn mô hình hóa chiếm dụng mà không có một bản đồ ổn định sẽ khiến đơn hàng trỏ vào hư không.

Increment này tách hai thứ rạch ròi: một mô hình sàn ổn định, theo merchant (layout / zone / unit) được tạo dưới dạng cây nguyên tử, và một bản ghi usage trực tiếp gắn một đơn hàng với một hoặc nhiều unit, điều khiển màu của mỗi ô trên sơ đồ sàn, và là nguồn sự thật cho tính khả dụng, chuyển bàn, tách và gộp.

2. Goals & Non-Goals

Goals

  • Mô hình hóa sàn vật lý của một merchant thành đồ thị layout → cây zone → unit có tên, với unit mang sức chứa, vị trí canvas và style.
  • Tạo cả sàn trong một aggregate nguyên tử (create và smart-update), với giới hạn độ sâu và một guard kiểm tra zone thuộc đúng layout.
  • Chiếm dụng unit bằng cách mở một đơn dine-in; điều khiển một sơ đồ sàn trực tiếp hiển thị mỗi unit trống hay bận.
  • Tìm bàn trống - các unit khả dụng trong một zone, và các zone con mà mọi bàn đều trống.
  • Chuyển một nhóm khách giữa các zone, và giữ phân bổ đúng khi một đơn bị tách hoặc gộp.
  • Phát một sự kiện thời gian thực trên mỗi thay đổi chiếm dụng; giữ một snapshot phân bổ trên đơn hàng.

Non-Goals

  • Đặt chỗ như một sản phẩm booking (danh sách chờ, đặt cọc, lịch) - mô hình usage hỗ trợ một principal đặt chỗ, nhưng trải nghiệm booking là một increment riêng.
  • Giá hay phí dịch vụ theo zone/bàn - giá nằm trong fares; phân bổ không mang tiền.
  • Một UI trình soạn sơ đồ sàn đồ họa - PRD này đặc tả mô hình và các thao tác của nó; công cụ canvas là việc của apps/sale-renderer.
  • KDS / điều phối bếp và bản thân vòng đời đơn hàng (Sale) - phân bổ tham chiếu một đơn, nó không sở hữu đơn.

3. Success Metrics

Chỉ sốMục tiêu / tín hiệu
Toàn vẹn sànMột bước con lỗi trong aggregate không để lại cây dựng dở - layout, zone và unit commit cùng nhau hoặc không gì cả
An toàn độ sâuKhông aggregate nào lưu một cây zone sâu hơn giới hạn hai cấp, hoặc một zone không thuộc layout của nó
Sự thật chiếm dụngMột unit hiển thị bận đúng khi nó có một usage reserved/active/success, và trống khi ngược lại
Chính xác khả dụng"Bàn trống" chỉ trả về các unit không có usage trực tiếp; một zone chỉ được đề xuất khi mọi bàn của nó đều trống
Đồng bộ trực tiếpMọi chiếm dụng / giải phóng / chuyển bàn phản ánh trên mọi máy POS mà không cần làm mới thủ công

4. Personas & Use Cases

PersonaMục tiêu trong tính năng này
Chủ / Quản lýBố trí sàn một lần - vùng, bàn, ghế, vị trí - và sửa khi không gian thay đổi
Phục vụ / Thu ngânXem bàn nào trống, xếp một nhóm khách, chuyển nhóm, và giải phóng bàn khi khách rời
Lễ tânĐọc sàn trực tiếp trong một cái liếc và chọn một bàn trống đúng kích cỡ

Kịch bản chính: chủ quán thiết kế "Tầng Trệt" thành một layout với hai phòng, mỗi phòng chứa vài bàn, trong một lần lưu. Trong ca, phục vụ mở một đơn dine-in trên Bàn 7; ô bàn chuyển sang bận trên mọi máy. Một nhóm sáu khách vãng lai đến - phục vụ lọc bàn trống chứa được sáu, xếp họ, rồi sau đó chuyển họ từ phòng chính ra sân vườn trong một thao tác. Khi mỗi nhóm rời đi, hoàn thành usage của đơn sẽ giải phóng bàn về xanh.

5. User Stories

  • chủ quán, tôi thiết kế sàn thành vùng và bàn trong một lần lưu, để bản đồ hoàn chỉnh và nhất quán ngay khi nó tồn tại.
  • chủ quán, tôi sửa layout - thêm phòng, bỏ bàn, đặt lại vị trí ghế - qua một smart-update mà không dựng lại.
  • phục vụ, tôi xem bàn trống và bận trực tiếp, để không bao giờ xếp khách vào bàn đang bận.
  • phục vụ, tôi mở một đơn trên một bàn và nó lập tức hiện bận với mọi người, để sàn không bao giờ bị bán trùng.
  • phục vụ, tôi chuyển một nhóm đang ngồi sang vùng khác trong một thao tác, và bàn cũ trống còn bàn mới đầy.
  • phục vụ, tôi hoàn thành một bàn khi khách rời và nó trống trở lại, để nhóm tiếp theo được xếp.

6. Functional Requirements

#Yêu cầuURD ref
FR-1Một merchant mô hình hóa sàn thành một layout có tên chứa một cây zone; zone lá sở hữu unit có sức chứa, vị trí và styleURD-FLR-001..003
FR-2Một layout được tạo cùng cả cây zone lồng nhau trong một aggregate nguyên tửURD-FLR-004
FR-3Một aggregate layout cập nhật theo quy ước id: chỉ-id = xóa (cascade), id+data = cập nhật, không-id = tạoURD-FLR-005
FR-4Một zone tự nó cũng create/update được như một aggregate với zone con và unit lồng nhau, và zone có thể được tạo hàng loạtURD-FLR-006
FR-5Độ sâu bị giới hạn ở hai cấp dưới gốc layout; cây sâu hơn, hoặc một zone không thuộc layout của nó, bị từ chối trước khi lưuURD-FLR-007
FR-6Một aggregate layout đọc được với độ sâu tối đa cấu hình được - cây đầy đủ cho back-office, nông cho POSURD-FLR-008
FR-7Layout, zone và unit mang một trạng thái vòng đời (Activated / Deactivated / Archived) và được soft-delete; mỗi cái cũng quản lý độc lậpURD-FLR-009..010
FR-8Mở một đơn dine-in trên một hoặc nhiều unit tạo một usage chiếm dụng đánh dấu các unit đó bậnURD-FLR-011
FR-9Một usage mang một khung thời gian đặt chỗ; nếu không cho thời điểm kết thúc, một khung mặc định 90 phút áp dụngURD-FLR-012
FR-10Chiếm dụng theo reserved/active → success → completed (giải phóng) hoặc cancelled; một usage terminal không thể bị hủy lạiURD-FLR-013
FR-11Một unit bận khi nó giữ một usage reserved/active/success và trống khi ngược lại; POS có thể truy vấn unit trống và zone con hoàn toàn trốngURD-FLR-014..015
FR-12Một nhóm có thể được chuyển giữa các zone - hủy usage nguồn, chiếm dụng các unit của zone đích cho mỗi đơn, nguyên tửURD-FLR-016
FR-13Tách một đơn nhân bản các usage đang hoạt động của nó sang mỗi đơn mới; gộp chuyển chúng sang đơn còn sốngURD-FLR-017
FR-14Mỗi thay đổi chiếm dụng phát một sự kiện sơ đồ sàn thời gian thực tới mọi máyURD-FLR-018
FR-15Một usage có thể ghi thông tin khách, và đơn/đặt chỗ giữ một snapshot phân bổ (unit + đường dẫn zone + khách)URD-FLR-019..020
FR-16Mọi thao tác phân bổ scope theo merchant (x-merchant-id) và được gác bởi quyền phân bổURD-FLR-021

Toàn văn yêu cầu và tiêu chí chấp nhận nằm trong Commerce URD - FLR. PRD này tham chiếu thay vì lặp lại.

7. Non-Functional Requirements

Lĩnh vựcYêu cầu
AtomicityMô hình sàn (layout + zone + unit) và mỗi thao tác chiếm dụng (start, transfer, split, merge) đều là transaction tất-cả-hoặc-không
Depth safetyMột guard cây đệ quy giới hạn độ sâu zone và từ chối tham chiếu zone không thuộc layout trước khi ghi gì
Real-timeThay đổi chiếm dụng đẩy sự kiện trực tiếp để mọi máy POS phản ánh sàn mà không poll
Tenancy & authzMọi thao tác scope theo merchant (x-merchant-id); thiết kế sàn gác bởi quyền allocation-layout/zone/unit, chiếm dụng bởi quyền allocation-usage
PerformanceKhả dụng giải các unit của một zone qua một truy vấn đệ quy đơn; đọc sàn giới hạn độ sâu nên POS chỉ lấy phần nó render
DurabilityMỗi đơn giữ một snapshot phân bổ (unit + đường dẫn zone + khách) để phân bổ tồn tại trên tài liệu độc lập với các dòng usage trực tiếp
i18nTên layout, zone và unit song ngữ ({ en, vi })

8. UX & Flows

Vòng đời chiếm dụng

Xếp khách, xem trực tiếp, chuyển bàn

Mô hình sàn hiện ở back-office như một trình soạn layout (vùng, bàn, sức chứa, vị trí) và ở POS (apps/sale-renderer) như sơ đồ sàn trực tiếp tô màu mỗi ô theo chiếm dụng và điều khiển xếp / tìm-trống / chuyển / hoàn thành.

9. Data & Domain

Thực thểVai trò
AllocationLayoutBản đồ sàn có tên của một merchant; gốc của cây zone; mang style canvas
AllocationZoneMột vùng trong cây (nhóm sàn / phòng / bàn); tự lồng tối đa hai cấp dưới layout
AllocationUnitMột đơn vị có thể ngồi (bàn); mang sức chứa, vị trí canvas và style; thuộc một zone lá
AllocationUsageMột chiếm dụng trực tiếp gắn một unit với một đơn hàng (hoặc đặt chỗ), với một khung đặt chỗ và một trạng thái điều khiển màu sàn

Chỉ mang tính khái niệm - schema đầy đủ và bất biến nằm trong commerce domain model. Quan hệ là tham chiếu mềm; toàn vẹn được thực thi trong service aggregate và usage, không phải bởi ràng buộc cơ sở dữ liệu.

10. Dependencies & Assumptions

Phụ thuộc vào

  • Merchant (MER, PRD-ORG-001) - mọi layout, zone, unit và usage scope theo một merchant.
  • Đơn hàng (Sale) - một usage chiếm unit thay cho một đơn dine-in; tách / gộp / thanh toán điều khiển vòng đời của nó.
  • @nx/core - các model thực thể phân bổ, trạng thái, và kênh socket trực tiếp.

Giả định

  • Loại hình kinh doanh của merchant là phục vụ tại bàn (F&B); merchant counter / bán lẻ không cần mô hình sàn.
  • Độ sâu zone thực tế nhiều nhất là sàn → phòng → bàn; giới hạn hai cấp phản ánh điều đó.
  • Một đơn hàng là principal chiếm dụng thông thường; principal đặt chỗ được mô hình hỗ trợ nhưng UX booking của nó ngoài phạm vi ở đây.

11. Risks & Open Questions

Rủi ro / câu hỏiGiảm thiểu / trạng thái
Một sàn dựng dở trên aggregate lỗi (có zone nhưng không unit)Cả cây là một transaction - bất kỳ lỗi nào cũng rollback hoàn toàn
Một cây zone chạy loạn hoặc lặp vòngĐộ sâu giới hạn hai cấp và tham chiếu không thuộc layout bị từ chối trước khi lưu
Hai nhóm xếp vào một bàn (bán trùng)Một unit bận khi tồn tại bất kỳ usage reserved/active/success; khả dụng loại trừ unit đang bận
Sơ đồ sàn lệch giữa các máyMỗi thay đổi chiếm dụng phát một sự kiện trực tiếp được mọi máy POS tiêu thụ
Mất phân bổ nếu các dòng usage thay đổiMỗi đơn giữ một snapshot phân bổ (unit + đường dẫn zone + khách) trên tài liệu của riêng nó
Bàn hết khung giờ không bao giờ được giải phóngMột khung đặt chỗ mặc định 90 phút được ghi; hoàn thành giải phóng bàn một cách tường minh

12. Release Plan & Launch Criteria

Khía cạnhKế hoạch
PhaseP2 - FLR trong URD feature catalog
RolloutMerchant F&B / dine-in; không feature flag
MigrationKhông - thực thể phân bổ mới; merchant hiện hữu nhận một mô hình sàn rỗng
Launch criteriaMột layout với cây zone và unit lưu nguyên tử; guard độ sâu và thuộc-layout giữ vững; mở một đơn dine-in chiếm unit và chuyển chúng bận trực tiếp; truy vấn bàn trống loại trừ unit đang bận; chuyển bàn di chuyển một nhóm nguyên tử; hoàn thành giải phóng bàn; mỗi đơn giữ snapshot phân bổ
MonitoringTỷ lệ lỗi aggregate theo lý do (độ sâu, không thuộc layout), độ trễ giao sự kiện chiếm dụng, độ trễ truy vấn khả dụng

13. FAQ

Tại sao bản đồ sàn và chiếm bàn tách biệt? Vì chúng thay đổi với tần suất hoàn toàn khác nhau. Bản đồ là một chỉnh sửa back-office hiếm hoi; chiếm dụng xáo trộn mỗi lần xếp khách. Giữ chúng tách biệt nghĩa là sàn trực tiếp không bao giờ ghi đè bản đồ, và đơn hàng luôn trỏ vào một unit ổn định.

Sàn của tôi sâu được bao nhiêu? Hai cấp dưới gốc layout - thực tế là sàn → phòng → bàn. Một aggregate sâu hơn, hoặc trỏ một zone vào sai layout, bị từ chối trước khi lưu gì.

Khi nào một bàn hiện bận? Khi nó giữ một usage là reserved, active, hoặc đã trả (success). Khi usage đó được hoàn thành hoặc hủy, bàn trống trở lại.

Chuyện gì xảy ra với bàn cũ khi tôi chuyển một nhóm? Việc chuyển là nguyên tử: usage nguồn bị hủy và các unit của zone đích được chiếm cho mỗi đơn liên quan, nên bàn cũ trống và bàn mới đầy cùng nhau.

Tôi có mất phân bổ bàn nếu usage trực tiếp thay đổi không? Không - mỗi đơn giữ một snapshot phân bổ của unit và đường dẫn zone của unit (cùng thông tin khách), nên phân bổ tồn tại trên chính đơn hàng.

Tôi có thể tìm một bàn trống đúng kích cỡ không? Có - POS truy vấn unit khả dụng trong một zone (và zone con mà mọi bàn đều trống), bạn có thể lọc theo sức chứa.

References

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.