Internal · Engineering Date · 2026-05-19 Repo · ns-pos Audience · Frontend + Backend team

POS Architecture &
Impact Reports

Một bộ tài liệu hợp nhất về tình trạng kiến trúc hiện tại của ns-pos, phân tích blast radius từng cụm tính năng, và hai hướng đi cho Calendar — sửa trong stack hiện tại hay viết lại native Android cho D3. Mục đích thực tế: giúp các phase công việc tiếp theo bắt đầu từ hình dạng thật của codebase, không phải từ một sơ đồ kiến trúc lý tưởng.

2,470
JS/TS files
9,445
Dependency edges
472
Files in Checkout cluster
9–12 mo
Full-gap program estimate
§ Index

Document Index

Bốn tài liệu cấu thành report tổng hợp này. Đọc theo thứ tự để có toàn cảnh, hoặc nhảy thẳng vào phần liên quan đến công việc đang làm.
Document Topic Pages
I Calendar Performance & Architecture Report Phân tích git-history, sơ đồ kiến trúc hiện tại, bản đồ technical debt theo module, và chiến lược hiệu năng Calendar trên thiết bị POS. §1–18
II Android D3 Native Rewrite Impact Plan Kế hoạch và estimate dựa trên impact analysis cho việc viết lại app POS thành Android D3 native với stack mới. §1–10
III Impact Analysis · Hướng dẫn sử dụng Cách dùng bộ công cụ phân tích blast radius nội bộ để estimate độ phức tạp và rủi ro trước khi làm việc. Guide
IV Refactor Calendar Slice · Plan Template Mẫu plan chi tiết cho lần refactor calendar slice tiếp theo, kèm impact analysis có sẵn. Template
⌘ Cách đọc

Nếu bạn là FE/BE engineer mới vào project, đọc lần lượt I → II → III. Nếu bạn đang chuẩn bị một refactor cụ thể, vào III trước rồi quay lại I/II khi cần. Tài liệu IV là template để bạn dùng cho từng PR/ticket lớn.

Document I · Architecture & Performance

Calendar Performance and Architecture Report

Phân tích toàn diện thread đã đi qua: từ lịch sử git log, sơ đồ debt theo module, kiến trúc source hiện tại, đến hai hướng giải pháp cho Calendar trên POS — kèm critique trung thực cho cả hai.

Repository: ns-pos · Primary focus: src/salon/SalonHomeScreen/pages/CalendarPagesrc/redux/slices/salon/calendar

Tài liệu này tập hợp toàn bộ phân tích bao gồm:

  • tiến hóa của project từ git log
  • bản đồ technical debt theo module
  • kiến trúc source code hiện tại
  • hiệu năng Calendar trên thiết bị POS như SUNMI D3 Pro
  • kiến trúc Calendar mục tiêu, có và không có ràng buộc về tech stack hiện tại
  • critique cho cả hai hướng giải pháp

Mục đích thực tế: giúp các công việc tương lai bắt đầu từ hình dạng thật của codebase, không phải từ một sơ đồ kiến trúc lý tưởng.

§ 01Executive Summary

ns-pos là một React Native POS modular monolith. Cấu trúc bên ngoài hướng domain: Salon, Retailer, Merchant, shared platform code. Bên trong là một kiến trúc lai tích tụ qua nhiều giai đoạn:

  • legacy Redux actions/reducers
  • Redux Toolkit slices
  • redux-saga orchestration
  • axios/request routes
  • React Query cache bridge
  • RTK Query
  • global providers cho SignalR, modal, network, push, CodePush

Calendar là module rủi ro cao nhất và churn cao nhất trong codebase. Nó không chỉ là một màn hình — nó là bề mặt vận hành chính của Salon POS app:

  • appointment timeline
  • staff columns
  • waiting / any-staff flows
  • drag/drop scheduling
  • realtime SignalR updates
  • checkout / ticket-view coupling
  • caller ID coupling

Trên các thiết bị Android POS như SUNMI D3 Pro, vấn đề performance của Calendar phần lớn là cấu trúc. Thiết bị không phải là vấn đề duy nhất. Source hiện tại đang yêu cầu JS/UI system làm quá nhiều việc cho mỗi update:

  • tính toán layout rộng trong calendar reducers
  • selectors đắt tiền rebuild các array/object lớn
  • horizontal scroll synchronized phức tạp
  • realtime events dispatch thẳng vào path update state nặng
  • render/draw qua nhiều cột trên màn hình FHD lớn
Khuyến nghị mạnh nhất ngay lúc này: trước tiên ổn định app hiện tại bằng cách giảm workload của Calendar trên mỗi update. Sau đó prototype một Calendar surface thế hệ mới nếu sản phẩm yêu cầu responsiveness native-grade POS.

§ 02Git History Roadmap

Lịch sử repository gợi ý rằng app không bắt đầu life trong repo này từ một first commit clean. Nó vào dưới dạng một snapshot khá lớn.

Quan sát quan trọng:

  • First commit quan sát được: c9abb28de vào 2023-10-02, message add source code sprint 30.
  • Tổng commits quan sát: khoảng 6,285.
  • Merge commits quan sát: khoảng 1,468.
  • Lịch sử sớm nhanh chóng vào chế độ hotfix quanh Calendar, checkout, undo check-in, booking, drag/drop, và payment.

2.1 · Phase tiến hóa sản phẩm

Phase 1 · Source snapshot và hot fixes, 2023-10 trở đi

Giai đoạn đầu trông giống như một source-code drop, theo sau là stabilization nhanh: Calendar loading, booking basket clearing, undo check-in APIs, drag/drop block time, appointment trả về sai vị trí gốc, customer note. Điều này nghĩa là lịch sử repo không thể đọc như birth thật của product.

Phase 2 · Breadth expansion, 2024

App mở rộng qua nhiều POS domain: marketing, caller ID, permissions, printer, payment, customer flows, reports/settings. Kiến trúc trong phase này bị thống trị bởi legacy Redux / action / reducer / saga và request routes.

Phase 3 · Checkout / Payment / Receipt growth, 2025

Từ giữa 2025 trở đi, checkout và payment trở nên nóng hơn nhiều: checkout selection, staff/service/category selection, basket local state, receipt, split/payment/tip/gift card, payment terminal flows. Calendar cũng tiếp tục nhận performance và scroll fixes.

Phase 4 · Refactor có chủ đích hơn, cuối 2025 đến 2026

Lịch sử bắt đầu thể hiện GitHub-style pull-request flow nhiều hơn và refactor work explicit hơn: calendar saga refactor, ticket view refactor, staff filtering, checkout selection refactor, printer/Sunmi work, iOS Calendar scroll stability specs.

2.2 · Process issues nhìn thấy trong git log

  • Nhiều merge commits gợi ý long-lived branches và conflict resolution lặp lại.
  • Commit messages thường mất context: ., clean code, push code ., message chỉ có Jira link.
  • Release/version/build commits trộn vào development history.
  • Reverts và rework cycles xuất hiện quanh nhiều business flows.
  • Calendar và Checkout bugs lặp lại qua các năm, gợi ý coupling sâu và thiếu regression coverage.

Những vấn đề này không nói team cẩu thả — chúng thường phản ánh một POS product chuyển động nhanh dưới áp lực vận hành thực tế. Nhưng kết quả là traceability kém và integration cost cao.

§ 03Module Debt Map

Xếp hạng module debt từ review trước:

  1. Calendar
  2. Checkout
  3. Printer
  4. Payment
  5. Caller ID

3.1 · Calendar Hottest

Calendar là hotspot top.

Core files

  • src/salon/SalonHomeScreen/pages/CalendarPage/useProps.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/components/CalendarBody/index.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/components/CalendarHeader/index.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/hooks/useScrollSync.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/hooks/useHandleAfterDroppingBlock.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/hooks/useHandleSignalCalendar.js
  • src/redux/slices/salon/calendar/index.js
  • src/redux/slices/salon/calendar/selectors.js
  • src/redux/saga/calendar/calendar.js

Debt type

  • high churn
  • broad recomputation
  • scroll / gesture complexity
  • realtime coupling
  • checkout / ticket / caller coupling
  • large reducer và hook files

Vì sao là module nóng nhất

  • Là màn hình vận hành chính của Salon POS.
  • Kết hợp UI, realtime, business rules, drag/drop, và navigation/modal workflows.
  • Một state update nhỏ có thể ảnh hưởng phần lớn surface được render.

3.2 · Checkout Revenue Core

Checkout là hotspot thứ hai.

Core files

  • src/salon/SalonHomeScreen/components/checkouts/widgets/categories/ServiceSelectionPanel/index.js
  • src/salon/SalonHomeScreen/components/checkouts/widgets/categories/ServiceSelectionPanel/components/ItemSelectionList.js
  • src/salon/SalonHomeScreen/components/checkouts/widgets/categories/StaffSelectionPanel/components/StaffList.js
  • src/salon/SalonHomeScreen/components/checkouts/widgets/baskets/BasketAppointmentList.js
  • src/redux/saga/checkout/selection/refreshCheckoutItems.js
  • src/redux/slices/salon/checkoutSelection/index.js

Debt type

  • state selection coupling
  • cache invalidation complexity
  • category / staff / service sync
  • basket coupling
  • UI và business flow trộn qua hooks/sagas

Checkout structurally ít dominant hơn Calendar, nhưng là revenue core. Bugs ở đây impact rất cao.

3.3 · Printer Integration-heavy

Core files

  • src/shared/hooks/printer/usePrinter.js
  • src/shared/hooks/printer/useEpson.js
  • src/shared/hooks/printer/usePrinterManager.js
  • src/shared/components/Receipts/PopupReceipt.js
  • src/shared/components/Receipts/widgets/ReceiptTotal.js

Debt type

  • hardware / vendor-specific behavior
  • Epson / Sunmi / receipt XML / cash drawer / reconnect paths
  • operational risk chứ không phải UI complexity thuần túy

Printer trở nên đặc biệt active quanh 2026.

3.4 · Payment Coupled to Checkout

Payment ở mức medium nếu xét riêng, nhưng coupling cao với Checkout.

Core files

  • src/shared/hooks/checkout/usePayBasket.js
  • src/shared/hooks/checkout/usePaymentZCP.js
  • src/salon/SalonHomeScreen/components/checkouts/widgets/payments/PaymentNumberPad.js
  • src/shared/components/Payments/DialogPayCompleted.js
  • src/shared/components/Payments/DialogPromotionDiscount.js

Debt type

  • split payment, tip, redeem, cash permission, transaction fee, terminal device integration, receipt handoff

Payment bugs thường xuất hiện dưới dạng checkout hoặc receipt bugs.

3.5 · Caller ID Hidden in Calendar

Caller ID nhỏ về file count, nhưng debt của nó ẩn trong Calendar hooks.

Core files

  • src/shared/hooks/signalr/useSignalR.js
  • src/shared/providers/SignalProvider.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/hooks/useHandleSignalCalendar.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/hooks/useShowAppointmentDetail.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/components/AppointmentDetailModal/CallerIdBookingModal.js

Debt type

  • realtime lifecycle, popup/modal state, Calendar coupling, checkout basket cleanup

Caller ID nên được xem như một runtime event domain, không chỉ là Calendar sub-feature.

§ 04Current Architecture Map

4.1 · App Shell

App.tsx
└─ src/index.js
   ├─ CodePushProvider
   ├─ Redux Provider(store)
   ├─ PersistGate(persistor)
   ├─ PushyProvider
   ├─ ReactQueryProvider
   ├─ NetworkProvider
   └─ RootNavigator
      ├─ ThemeProvider
      ├─ NavigationContainer
      ├─ MainStack
      │  └─ ModalProvider
      │     ├─ AuthNavigator
      │     ├─ SalonNavigator
      │     │  └─ SignalProvider
      │     │     └─ AppWrapper
      │     │        └─ Salon drawer screens
      │     ├─ RetailerNavigator
      │     │  └─ SignalProvider
      │     │     └─ AppWrapper
      │     │        └─ Retailer drawer screens
      │     └─ SetupStoreStack
      ├─ PopupPairingCode
      ├─ RightMessagePanel
      └─ AppLoading

4.2 · Store

Redux store
├─ configureStore()
├─ redux-saga
├─ redux-persist
├─ redux-batched-actions
├─ reducers = combine(
│  ├─ legacy reducers: src/redux/reducers/*
│  └─ RTK slices: src/redux/slices/*
│
└─ sagaRoot()
   ├─ app
   ├─ auth
   ├─ category
   ├─ product
   ├─ staff
   ├─ service
   ├─ extra
   ├─ upload
   ├─ appointment
   ├─ customer
   ├─ invoice
   ├─ marketing
   ├─ report
   ├─ review
   ├─ orderRetail
   ├─ hardware
   ├─ settlement
   ├─ dataLocal
   ├─ calendar
   ├─ request
   └─ checkout
▲ Important source fact

Store combine RTK slices và legacy reducers cùng nhau. Đây không phải hybrid lý thuyết — đó là state architecture thật.

4.3 · Data Layer

Ba thế hệ data-access tồn tại song song.

Legacy request path

component/hook
  -> redux action
  -> saga
  -> src/apis/request.js
  -> src/apis/routes/*
  -> axiosClient

React Query bridge

saga/request helper
  -> requestWithCache()
  -> queryClient.current.fetchQuery(...)

RTK Query path

component/hook
  -> harmonyApi/reportApi endpoints
  -> axiosBaseQuery / axiosReportQuery

Kết luận: data access hôm nay chưa được unified.

4.4 · Realtime

Realtime là first-class, không phải add-on.

SignalProvider
└─ useSignalR()
   ├─ opens HubConnection
   ├─ receives events
   ├─ normalizes payload keys
   ├─ fans out to listeners
   └─ dispatches Redux / triggers app updates

SignalProvider đóng vai trò một runtime event hub.

4.5 · Modal System

Modal system cũng là một lớp ứng dụng:

MainStack
└─ ModalProvider
   ├─ modal stack
   ├─ modal registry
   ├─ STACK / REPLACE / SINGLE modes
   └─ useModalContext() callers across app

Nhiều business flows là modal-driven hơn là pure navigation-driven.

§ 05Calendar Current State

Calendar hiện tại trải dài UI, hooks, Redux slice, selectors, sagas, shared utilities, realtime handling, modal flows, ticket view updates, và checkout interactions.

5.1 · Calendar source map

Các file quan trọng:

  • src/salon/SalonHomeScreen/pages/CalendarPage/Layout.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/useProps.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/components/CalendarHeader/index.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/components/CalendarBody/index.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/components/CalendarBody/useProps.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/components/CalendarContainer/useProps.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/hooks/useScrollSync.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/hooks/useCalendarHorizontalScroll.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/hooks/useCalendarVerticalScroll.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/hooks/useHandleMovingBlock.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/hooks/useHandleAfterDroppingBlock.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/hooks/useHandleSignalCalendar.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/components/widgets/BackgroundLayer/index.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/components/widgets/AreaHitLayer/index.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/components/CalendarHeader/CalendarHeaderStaffItem.js
  • src/salon/SalonHomeScreen/pages/CalendarPage/components/PanGestureContainer.js
  • src/redux/slices/salon/calendar/index.js
  • src/redux/slices/salon/calendar/selectors.js
  • src/redux/slices/salon/calendar/Helpers.js
  • src/shared/utils/calendar/index.js
  • src/shared/utils/calendar/validate.js
  • src/redux/saga/calendar/calendar.js

5.2 · Quantitative observations

FileLines (approx)
CalendarHeader/index.js526
CalendarBody/index.js401
useScrollSync.js615
useHandleAfterDroppingBlock.js513
useHandleSignalCalendar.js360
calendar slice index.js1,522
calendar selectors.js277

Tín hiệu khác:

  • Số useSelector trong Calendar subtree: ~60.
  • Số JSON.stringify usages trong Calendar subtree: ~16.
  • FlashList tập trung ở useScrollSync.js, CalendarBody/index.js, và CalendarHeader/index.js.
  • Canvas usage trong components/widgets/BackgroundLayer/index.js.

Những con số này không tự động xấu. Nhưng cùng nhau, chúng chỉ ra một màn hình lớn, highly interactive với nhiều state subscriptions và render dependencies.

§ 06Calendar Performance Findings

6.1 · Full layout recompute is too common

Trong src/redux/slices/salon/calendar/index.js, layout được recompute qua các helper path như updateStaffLayout, gọi calculatorColumnStaffLayout.

Các action có thể trigger broad layout work:

  • setCalendarDataByDate
  • removeStaff
  • updateBlocksBookingOnline
  • updateTurnForStaffs
  • cancelAppointment
  • moveAppointmentToWaiting
  • updateAppointmentFromSignal
  • addAppointment
  • updateBlockTimes

Nghĩa là appointment add/update/cancel/move, block-time edits, và SignalR changes có thể gây ra nhiều recomputation hơn local change yêu cầu.

6.2 · Selectors rebuild too much

Trong src/redux/slices/salon/calendar/selectors.js, selectors như getStaffsAndBlockTimesDisplay map qua staff và filter blockTimes per staff:

staffs.map(staff => blockTimes.filter(block => block.staffId === staff.staffId))

getStaffDisplayAndAppointmentCount cũng deep-clone staff với JSON.parse(JSON.stringify(staffs)).

Đắt dưới điều kiện POS thông thường:

  • nhiều staff
  • nhiều blocks
  • realtime updates thường xuyên
  • màn hình FHD lớn hiển thị nhiều cột

Vấn đề không chỉ là cost thuật toán. Các selectors này còn tạo references object/array mới, tăng render churn.

6.3 · Layout algorithm is non-trivial

src/redux/slices/salon/calendar/Helpers.js tính: staff column widths, any-staff expansion, visible staff count, block layout per staff.

src/shared/utils/calendar/index.js tính overlap-related layout: max simultaneous blocks, block relations, width/left/top/height.

Logic này thuộc về một Calendar layout/projection engine. Hiện tại nó đang quá gần với hot Redux update paths.

6.4 · Scroll architecture is complex

vertical ScrollView
  -> horizontal body FlashList
  -> horizontal header FlashList
  -> synchronized via useScrollSync
  -> drag/gesture layer over blocks

useScrollSync.js điều phối: header/body leader/follower, animated refs, shared values, snapping, momentum end, programmatic scrolling, iOS smooth-scroll special cases.

Design hiểu được, nhưng đắt. Giải quyết bài toán synchronization khó bằng cách thêm control logic.

6.5 · iOS scroll stability got deep attention

Các commit quan trọng:

  • 591c89b39: APS-2696 Implement iOS calendar smooth scroll stability feature
  • 3751afdb7: Refine iOS calendar smooth scroll stability implementation
  • 26f5a79e8: Enhance iOS smooth scrolling behavior in CalendarPage
  • e39352391: Enhance iOS smooth scrolling logic in CalendarPage components

Spec dưới specs/002-ios-calendar-scroll-stability/ cho thấy tư duy cẩn thận về iOS smooth scroll, double snap, native snapToInterval, và programmatic scrollTo.

Đây là work hữu ích. Nhưng không phải bottleneck chính cho SUNMI D3. Với D3, vấn đề lớn hơn là JS work, render churn, và Android scroll/gesture stability.

6.6 · JSON.stringify memoization is a warning sign

Ví dụ:

  • CalendarHeaderStaffItem compare prevProps.itemnextProps.item bằng JSON.stringify.
  • PanGestureContainer dùng stringified reset params.
  • useCalendarVerticalScroll dùng JSON.stringify(workingTimes) trong dependencies.
  • useHandleSignalCalendar cũng depend trên stringified working times.

Style này có thể giảm một số identity churn. Nhưng thêm serialization cost và che giấu vấn đề sâu hơn: unstable data shape và broad object recreation.

6.7 · Realtime updates too close to heavy UI invalidation

useHandleSignalCalendar.js xử lý nhiều event types: appointment_add, appointment_update, change_item, appointment_checkout, staff_position, update_waiting, turn_update, staff_update_block_booking_online, edit_blocktime_updatecalendar, caller events.

Chúng dispatch vào calendar slice actions có thể recompute layout và invalidate UI. Trong giờ kinh doanh active, Calendar có thể feel inconsistent ngay cả khi user không trực tiếp tương tác.

6.8 · Drawing and GPU work add pressure

BackgroundLayer dùng Skia Canvas, Line, Rect cho grid/overlays. Mỗi staff column visible có thể có background drawing và block overlays.

DefaultListProps.CALENDARCalendarBody cũng dùng raster/texture flags như:

  • shouldRasterizeIOS
  • renderToHardwareTextureAndroid

Có thể giúp trong specific cases, nhưng trên Android POS hardware, chúng cũng có thể tăng texture memory pressure. Phải validate trên D3, không assume beneficial.

§ 07What the Previous Developers Got Right

✓ Good decisions

Work trước đó không random. Các quyết định tốt:

  • Dùng FlashList thay vì plain FlatList cho large horizontal lists.
  • Tạo useScrollSync để explicit control header/body synchronization.
  • Split calendar fetch thành phases trong src/redux/saga/calendar/calendar.js.
  • Dùng InteractionManager.runAfterInteractions trong saga helpers để defer dispatch.
  • Giảm callback identity churn trong hot scroll code.
  • Tạo dedicated iOS scroll-stability specs và research.
  • Hướng tới lower-touch granularity như AreaHitLayer.
  • Giữ Calendar-specific logic được group dưới CalendarPageredux/slices/salon/calendar.

Team rõ ràng hiểu rằng Calendar là một performance-sensitive screen.

§ 08What Is Wrong or Incomplete

Vấn đề chính không phải một component xấu — mà là pipeline:

Signal / API / user interaction
  -> broad Redux state update
  -> broad selector recompute
  -> broad layout rebuild
  -> broad React render
  -> complex scroll/gesture synchronization

Issues cụ thể:

  • Layout recomputation không đủ incremental.
  • Domain state và render layout state trộn quá nhiều.
  • Selectors scan arrays lặp lại thay vì đọc indexed state.
  • Realtime events áp dụng quá trực tiếp vào UI-sensitive paths.
  • Drag/drop có thể involve quá nhiều global state work.
  • Scroll authority trải qua native snap, programmatic snap, mirror sync, và Redux state.
  • Android POS requirements không bị isolated đủ khỏi iOS smooth-scroll complexity.

§ 09Debt Reduction Plan

Phase 1 · highest priority
Calendar Stabilization

Giảm regression trong drag/drop, selected date, ticket view, caller-driven update. Giảm full layout recompute. Thêm tests quanh pure calendar rules.

Actions
  • Tests cho calendar utility rules dưới src/shared/utils/calendar.
  • Tests cho src/redux/slices/salon/calendar.
  • Extract pure functions từ useHandleAfterDroppingBlock.js.
  • Giảm trách nhiệm của CalendarPage/useProps.jsAppointmentDetailModal/useProps.js.
High-value regression cases: move waiting → calendar, move giữa staff, block-time overlap, selected-date change, ticket view filtering/pagination, appointment-change confirm/revert.
Phase 2
Checkout Flow Hardening

Stabilize service/staff/category selection. Tách selection state khỏi scroll/UI state.

Actions
  • Normalize checkout selection state.
  • Review selectStaff.js, refreshCheckoutItems.js, useCategoryTabSync.js, ServiceSelectionPanel.
  • Tách fetch/cache, selection rules, và panel rendering.
Phase 3
Payment Rule Isolation

Isolate payment business rules khỏi checkout UI.

Actions
  • Group payment rules quanh hooks như usePayBasket.jsusePaymentZCP.js.
  • Tách split payment, tip, redeem, cash permission, transaction fee.
  • Define payment result model dùng cho checkout/receipt/calendar.
Phase 4
Printer Boundary Cleanup

Tách receipt model, printable payload, và device driver integration.

Actions
  • Normalize Epson/Sunmi adapters.
  • Giữ receipt display logic ra khỏi printer hooks.
  • Test receipt mapping và reconnect/retry behavior.
Phase 5
Caller ID Decoupling

Ngừng cho Caller ID live implicitly bên trong Calendar hooks.

Actions
  • Define caller session/event domain: incoming, active, booked, ended, cancelled.
  • Calendar consume caller state, không own whole caller flow.

§ 10Calendar Problem Redefined

Calendar không nên được treat như một complicated React screen. Nó là một real-time scheduling engine với một interactive UI surface.

Nó có ba responsibilities tách rời:

1 · Scheduling domain rules
  • staff
  • appointment
  • block time
  • waiting
  • any-staff
  • business hours
  • overlap
2 · Realtime state machine
  • SignalR updates
  • API responses
  • local optimistic state
  • conflict handling
  • batch/merge behavior
3 · Interactive surface
  • scroll
  • tap
  • drag/drop
  • snap
  • select
  • highlight
  • timeline

Câu hỏi cốt lõi về performance

Làm thế nào để mỗi change nhỏ force minimum necessary computation, và render chỉ minimum visible area?

§ 11Correct Solution While Keeping Current Tech Stack

Nếu React Native, Redux/Saga, FlashList, và Reanimated giữ nguyên, strategy đúng không phải "thêm memo". Phải redesign Calendar data/render pipeline.

11.1 · Target pipeline

Raw domain state
-> Indexed calendar state
-> Render layout model
-> UI surfaces

11.2 · Target state shape

Domain state

staffsById
staffOrder
appointmentsById
blocksById
workingTimesByStaffId
waitingAppointmentIds
anyStaffBlockIds

Indexed state

blockIdsByStaffId
appointmentIdsByStaffId
overlappingGroupsByStaffId
visibleStaffIds
visibleDate
calendarConfig

Render state

columnWidth
anyStaffColumnWidth
layoutByStaffId
waitingLayout
headerMetaByStaffId

Interaction state

scrollX
scrollY
draggingBlockId
dragPreview
selectedStaffId
selectedAppointmentId

Domain state ≠ render state.

11.3 · Incremental layout engine

Full recompute chỉ khi:

  • selected date thay đổi
  • business hours thay đổi
  • staff column mode thay đổi
  • visible staff structure thay đổi
  • calendar display mode thay đổi

Incremental recompute khi:

  • một appointment thay đổi time
  • một appointment thay đổi staff
  • một block time thay đổi
  • waiting list thay đổi
  • một staff turn/busy badge thay đổi

Ví dụ:

appointment moves staff A -> staff B
  -> invalidate staff A layout
  -> invalidate staff B layout
  -> update header meta for A and B
  -> do not recompute the whole day

11.4 · Selector strategy

Primitive selectors

  • selectStaffById
  • selectBlockIdsByStaffId
  • selectLayoutByStaffId

Per-column selectors

  • selectStaffColumn(staffId)
  • selectHeaderMeta(staffId)
  • selectWaitingColumn()

Viewport selectors

  • visible staff IDs
  • current page/range
  • visible column render models

Render tree nên subscribe theo cột, không theo whole Calendar model.

11.5 · Render strategy

Think Calendar như bốn surfaces:

1. static background grid
2. header strip
3. dynamic block layer
4. gesture/interaction overlay

Trên D3:

  • background càng static càng tốt
  • header data giữ light
  • blocks render chỉ cho visible columns
  • drag preview sống trong local/Reanimated state
  • Redux commit ở start/end/validation boundaries, không every frame

11.6 · Scroll strategy

Phải có một horizontal scroll authority.

Cho Android POS:

body owns horizontal scroll X
-> header mirrors X
-> Redux receives committed page/index only when needed

Tránh để native snap, programmatic settle, mirror sync, và Redux offset updates đánh nhau trong cùng một gesture.

11.7 · Realtime strategy

Current SignalR handling phải route qua một queue nhỏ:

Signal events
-> normalize
-> coalesce for 16-50ms
-> merge by entity/staff/date
-> apply one batch update
-> invalidate affected columns only

Quan trọng vì realtime bursts là normal POS load, không phải edge cases.

11.8 · Drag/drop strategy

Drag = UI simulation. Drop = state mutation.

During drag

  • dùng local/Reanimated state
  • dùng indexed collision lookup
  • tránh continuous Redux dispatch

On drop

  • validate
  • commit một action
  • recompute chỉ affected staff/waiting/any-staff layout

§ 12Correct Solution Without Tech Stack Constraints

Nếu tech stack không cố định và chỉ platform cố định, design tốt nhất thay đổi.

Platform assumptions

  • Android POS là target chính.
  • SUNMI D3 Pro có 15.6-inch FHD display, Qualcomm 6-core CPU, 4GB RAM, Android 13.
  • Device chạy POS app cho long sessions.
  • Touch responsiveness quan trọng hơn visual flourish.
  • Realtime updates trong giờ kinh doanh là bình thường.

12.1 · Target architecture

Backend / Realtime
-> Event Ingestion Layer
-> Domain Store
-> Projection Engine
-> Surface Adapter
-> Native Calendar Surface

12.2 · Calendar domain engine

Owns business truth: staff, appointment, block time, waiting, any-staff, working hours, availability, turn/busy state.

Không own pixels hay UI components.

12.3 · Projection engine

Turn domain state thành renderable layout: visible columns, block positions, overlap groups, waiting lane, any-staff lane, header badges, drag targets.

Phải support dirty-region recompute.

12.4 · Native Calendar surface

Cho best D3 responsiveness, Calendar phải là một dedicated interactive surface:

one scrollable body surface
+ header strip
+ block overlay layer
+ drag layer
+ lightweight timeline layer

Important properties:

  • one scroll authority
  • render by viewport
  • gesture runtime local to UI thread
  • background/grid static drawing nơi có thể
  • không full UI tree rerender cho small business updates

12.5 · Best stack direction nếu rebuild

Strongest platform-first option:

  • Android-native Calendar surface
  • Kotlin domain/projection engine, hoặc Kotlin Multiplatform nếu share với iOS quan trọng
  • App shell có thể remain React Native hoặc migrate riêng

Alternative options:

  • RN shell + native Calendar module
  • Kotlin Multiplatform domain engine + native Android/iOS surfaces
  • Flutter custom render surface, nếu broader UI migration chấp nhận được
Key idea: Share scheduling/projection engine nơi nào nó giúp ích. Không force the hottest interactive surface qua một abstraction không thể meet latency target.

§ 13Critique of Both Solution Directions

13.1 · Critique · Keep current tech stack

Hướng này thực tế và có lẽ là best short-term product move.

+ Strengths
  • ít disruption tổ chức nhất
  • fastest path đến measurable improvement
  • works với current app shell, navigation, modal, checkout, ticket view
  • dễ migrate incrementally
  • dùng existing team knowledge
− Weaknesses
  • có thể chỉ chuyển Calendar từ "bad" sang "acceptable"
  • React Native vẫn có runtime/component overhead
  • scroll sync vẫn là hard RN problem
  • Redux vẫn có thể bị misuse cho interactive state
  • heavy React component trees vẫn jank với large datasets
Đây là recovery path đúng cho current product, nhưng không nhất thiết là kiến trúc tốt nhất có thể cho một POS Calendar.

13.2 · Critique · Remove tech stack constraints

Hướng này cleaner về chiến lược cho long-term POS surface.

+ Strengths
  • higher performance ceiling
  • kiểm soát tốt hơn scroll, gesture, drawing, viewport, dirty regions
  • phù hợp Android POS constraints
  • có thể isolate Calendar như một purpose-built scheduling surface
− Weaknesses
  • implementation cost cao
  • parity risk cao
  • nhiều hidden business rules phải được rediscovered
  • debugging cross native/RN/realtime boundaries
  • Android-only native có thể tạo iOS behavior divergence
  • rewrite dễ dàng trở thành một large platform project
Đây là đúng cho Calendar generation-two strategy, không phải cho một quick performance rescue.

13.3 · Shared critique

Cả hai hướng có thể fail nếu không measure trên real hardware.

Required measurements

  • reducer/layout time per action
  • selector recompute count
  • React render count per scroll/drag/signal
  • JS frame drops
  • UI thread frame drops
  • memory và GC over long sessions
  • worst-case merchant dataset
  • realtime burst behavior

Architecture alone is not proof. D3 measurements phải quyết định team đi xa đến đâu.

§ 14Impact-Based Re-estimate

Sau khi rebuild local dependency graph và chạy yarn graph:impact trên các cluster kiến trúc chính, estimate trở nên conservative hơn.

Measured depth-3 blast radius

Area Seed files Affected Lines Interpretation
Calendar calendar/index.js · CalendarBody/index.js · CalendarPage/useProps.js 344 ~71k Very high. Reaches Ticket, Receipt, Modal, Printer, Appointment detail.
Checkout/Basket basketLocal/index.js · checkoutSelection/index.js · refreshCheckoutItems.js 472 ~110k Highest domain blast radius. Crosses Salon, Retailer, receipt, Calendar, selection UI.
Payment payment/index.js · usePayBasket.js · usePaymentZCP.js 343 ~71k Similar spread to Calendar — rides on checkout và receipt paths.
Printer/Receipt usePrinter.js · usePrinterManager.js · PopupReceipt.js 248 ~75k Fewer files than Checkout, nhưng nhiều file lớn và hardware-specific risks.
Signal runtime SignalProvider.js · useSignalR.js 26 ~6k Nhỏ hơn dự kiến. Candidate tốt cho early cleanup.
Data layer request.js · useHarmonyQuery.js · useHarmonyMutation.js · RTKQuery/baseApi 35 ~9.7k Modest, nhưng foundational. Vẫn phải treat carefully.
Store/state root redux/store/index.js · redux/slices/index.js · redux/reducers/index.js · redux/saga/index.js 610 ~143k Highest systemic blast radius. Tránh early direct refactor.
Modal/app shell ModalProvider.js · MainStack.js · RootNavigator.js 494 ~120k Global interaction layer. Rủi ro cao cho mọi broad change.

Important central hubs từ graph

FileDependents
src/shared/themes/index.js817
src/utils/index.js813
src/resources/index.js499
src/shared/providers/ModalProvider.js183
src/redux/slices/index.js164
src/apis/index.js155
▲ Cảnh báo

Các hub trên không phải good early refactor entry points. Chúng amplify blast radius quá nhanh.

§ 15Revised Roadmap

4-8 tuần Calendar rescue trước đó vẫn valid như một narrow workstream. Nhưng "refactor toàn bộ architecture gaps trong khi giữ stack" phải được đọc như một staged program, không phải single epic.

Phase 1
Foundation & Measurement
3–5 weeks
  • Performance baseline và parity gates
  • Feature flags và rollback paths
  • Thêm test, test:calendar, test:checkout, test:domains
  • Document target domain boundaries
  • Define RTK Query migration policy
Phase 2
Signal Runtime & Calendar
10–14 weeks
  • Normalize Calendar state và projection flow
  • Calendar event queue / coalescing
  • Simplify Android scroll authority
  • Giữ Ticket View, Caller ID, Checkout handoff parity
Rationale: Signal runtime có blast radius nhỏ, unlock safer Calendar batching.
Phase 3
Checkout & Basket
10–14 weeks
  • Tách selection state khỏi UI/cache state
  • Normalize basket model
  • Isolate refresh/invalidation rules
  • Giữ legacy UI qua adapters cho đến khi parity passes
Rationale: Impact cho thấy Checkout/Basket là broadest hotspot — likely là phase đắt nhất sau Calendar.
Phase 4
Payment
6–8 weeks
  • Define payment result contract
  • Isolate payment rules khỏi checkout widgets
  • Align checkout, payment, receipt trên một shared model
Phase 5
Printer · Receipt · Caller
10–14 weeks
  • Split receipt view model, printable payload, printer driver adapters
  • Move caller lifecycle vào real runtime domain
  • Preserve receipt và print parity qua Salon và Retailer flows
Phase 6
Shared, Data Layer & Remaining
8–12 weeks
  • Migrate high-churn APIs sang RTK Query adapters
  • Move domain-specific hooks ra khỏi shared/hooks
  • Clean up Customer, Inventory, Settings, Retailer boundaries
  • Remove compatibility paths chỉ sau stable parity

§ 16Updated Estimate

ScopeTeamEstimate
Foundation, Calendar, Signal runtime, Checkout/Basket, part of Payment/Printer boundary 2–3 senior engineers 6 months
Toàn bộ gaps (Customer/Inventory/Settings/Retailer cleanup, broad shared/data-layer migration) 2–4 engineers 9–12 months
Performance rescue + architecture foundation only (narrower) 3 months

Workstream-level estimate

WorkstreamEstimate
Foundation + governance + tests + baseline3–5 weeks
Calendar refactor8–12 weeks
Signal runtime / event queue2–4 weeks
Checkout / Basket refactor10–14 weeks
Payment result / rules boundary6–8 weeks
Printer / Receipt adapter boundary8–10 weeks
Caller domain extraction4–6 weeks
Data-layer modernization across core domains10–16 weeks, parallel by domain
Shared-layer cleanup8–12 weeks, sau khi domain migration land

Overall effort: khoảng 50–70 engineering-weeks cho core program, khoảng 9–12 tháng nếu goal thật sự bao gồm đa số identified gaps, migration cleanup, và parity hardening.

§ 17Practical Next Steps

  1. Giữ .impact-graph.json local. Rebuild sau major refactors. Không commit file này.
  2. Bắt đầu với low-blast-radius enablers: Signal event queue, feature flags, baseline harness, domain adapters.
  3. Tránh early direct changes vào:
    • redux/store/index.js
    • redux/slices/index.js
    • ModalProvider.js
    • RootNavigator.js
  4. Dùng impact analysis trước mỗi phase lớn để confirm blast radius chưa mở rộng bất ngờ.
  5. Treat Calendar và Checkout như hai program tách rời nhưng chồng lấn, không phải một merged refactor task.

Engineering principle

Make each user-visible change do less work, and keep large-scale refactors away from global blast-radius amplifiers until the domain boundaries are already in place.

§ 18Short Glossary

TermMeaning
Domain stateSource-of-truth business data: staff, appointments, block times, waiting list, working hours.
Projection / render modelDerived data cần để draw Calendar: block positions, column widths, overlap groups, header badges.
Incremental layoutRecomputing chỉ staff/columns affected by a change, thay vì recalculating whole day.
Scroll authoritySingle component/system own scroll position trong một gesture.
CoalescingGom nhiều realtime updates thành một UI update, để interface không react theo từng packet.
Document II · Platform Rewrite Plan

Android D3 Native Rewrite Impact Plan

Kế hoạch tổng thể cho việc rebuild POS app cho Android D3 với frontend stack mới, dùng impact analysis chỉ để hiểu feature coupling và độ phức tạp — không phải để tái sử dụng source React Native.

Target platform: Android POS, SUNMI D3 class devices · Team assumption: 3 FE + 1 BE · Source for complexity: yarn graph:build + yarn graph:impact

§ 01Executive Summary

Rewrite nên Android-first và D3-only cho initial target. Hướng mạnh nhất:

Native Android Kotlin app
+ POS BFF/backend adapter
+ parity suite against existing app
+ D3 performance gates

Recommended frontend stack

  • Kotlin
  • Jetpack Compose cho app shell, forms, checkout, settings, và standard screens
  • Custom Android View/Canvas cho Calendar nếu Compose benchmarks không đủ
  • Coroutines + Flow
  • Room cho local cache và normalized local state
  • Hilt cho dependency injection
  • Retrofit/OkHttp cho APIs
  • SignalR/WebSocket adapter cho realtime
  • MVI/unidirectional state cho feature state

Recommended backend direction

  • Build BFF/backend adapter thay vì rewrite all backend systems.
  • Nếu existing backend/realtime Microsoft/SignalR-heavy, prefer .NET BFF.
  • Nếu team operations ưu tiên JS/TS, NestJS có thể work, nhưng backend contract clarity quan trọng hơn framework choice.
Calendar là lý do để rewrite, nhưng Checkout/Basket là largest business coupling cluster. Rewrite phải prove Calendar performance sớm, sau đó nhanh chóng validate Calendar → Checkout → Payment → Receipt/Printer như real POS operating path.

§ 02Impact Findings From Current App

Impact graph dùng để estimate feature coupling. Không imply source reuse.

Feature cluster Blast radius Rewrite implication
Calendar344 files / ~71k linesKhông thể rebuild như isolated screen; links to AppointmentDetail, Ticket, Checkout, Receipt, Caller.
Checkout/Basket472 files / ~110k linesLargest core business-rule cluster. Phải treat như major domain, không phải panel.
Payment343 files / ~71k linesCoupling chặt với Checkout, Receipt, Printer, device flows.
Printer/Receipt248 files / ~75k linesHardware và receipt behavior carry high operational risk.
Customer206 files / ~64k linesCustomer cần sớm vì booking, checkout, modal flows phụ thuộc.
Modal/Shell494 files / ~120k linesCurrent app dùng global modal như một workflow framework. New app cần explicit flow orchestration.
Signal runtime26 files / ~6k linesBlast radius nhỏ hơn dự kiến; có thể redesign early và cleanly.
Data layer35 files / ~9.7k linesGraph radius modest, nhưng BFF/API contract risk vẫn high.
Inventory30 files / ~6.4k linesCó thể defer nếu Android D3 v1 là Salon POS core.
Retailer15 files / ~4.5k linesKhông nên trong MVP trừ khi D3 explicitly require Retailer mode.

Important overlaps

Cluster pairOverlap (files)
Calendar & Checkout340
Calendar & Payment340
Checkout & Printer236
Checkout & Customer200
Printer & Customer191
Modal/Shell & Checkout331

Interpretation

  • Splitting work by screen sẽ underestimate complexity.
  • Splitting by vertical operating flow safer.
  • Path quan trọng nhất không phải "Calendar screen" — là Calendar → Appointment Detail → Checkout → Payment → Receipt/Printer.

§ 03Target Architecture

D3 Android App
├─ App Shell
│  ├─ Auth/session
│  ├─ Navigation
│  ├─ D3 device profile
│  ├─ Global error/loading/dialog handling
│  └─ Observability/performance harness
├─ POS Domains
│  ├─ Calendar
│  ├─ Appointment Detail
│  ├─ Checkout/Basket
│  ├─ Payment
│  ├─ Receipt/Printer
│  ├─ Caller
│  ├─ Customer
│  ├─ Minimum Settings
│  └─ Inventory/Retailer later if required
├─ Local Data Layer
│  ├─ Room cache
│  ├─ normalized entities
│  ├─ sync metadata
│  └─ realtime event queue
├─ Integration Layer
│  ├─ API client
│  ├─ SignalR/WebSocket client
│  ├─ payment terminal adapters
│  ├─ printer adapters
│  └─ device capability adapters
└─ BFF / Backend Adapter
   ├─ aggregate APIs
   ├─ normalized response shape
   ├─ realtime event contract
   └─ compatibility with existing backend services

§ 04Calendar Architecture

Calendar phải được build như một scheduling engine với một interactive surface, không phải như một normal screen.

Calendar Domain Model
-> Projection/Layout Engine
-> Native Calendar Surface
-> Gesture Runtime
-> Commit/Sync Pipeline

Rules

  • Domain state không chứa pixels.
  • Projection/layout engine support dirty-scope recompute.
  • Calendar có một scroll authority.
  • Drag/drop chạy local trên UI thread và commit state chỉ on drop.
  • Realtime events coalesced trước khi affect UI.
  • D3 performance là release gate, không phải nice-to-have.

Recommended implementation

  • Dùng Jetpack Compose cho surrounding controls và panels.
  • Dùng custom Android View/Canvas cho timeline surface nếu Compose không reliably hit D3 responsiveness target.
  • Giữ projection engine pure Kotlin và testable.

§ 05Roadmap

Phase 0
Parity & Contract Freeze
3–4 weeks
  • Confirm Android D3 v1 scope: Salon POS core only.
  • Build parity matrix cho Calendar, Appointment Detail, Checkout/Basket, Payment, Receipt/Printer, Caller, Customer basic, Minimum D3 settings.
  • Define D3 performance targets.
  • Define initial BFF/API contracts.
  • Define realtime event contract.
  • Create old-app benchmark fixtures.
Gate: Không large feature build nào start trước khi parity matrix và benchmark datasets exist.
Phase 1
Native Foundation & BFF
6–8 weeks

FE: App shell, Auth/session, Navigation, MVI base, API client, SignalR/WS client, Room/cache base, Error/loading framework, D3 device profile, Performance harness.

BE: BFF skeleton, Auth pass-through, Calendar aggregate endpoint draft, Normalized error/response, Realtime contract draft.

Gate: App login, call API, connect realtime, chạy benchmark harness trên D3.
Phase 2 · Owner: FE1 + BE1
Calendar Vertical Slice
12–16 weeks

FE: Calendar domain model, Projection/layout engine, Native Calendar surface, Staff columns, Appointment blocks, Waiting/any-staff, Selected date, Appointment detail entry, Drag/drop MVP, Realtime event batching, D3 benchmark.

BE: Calendar aggregate endpoint, Appointment update/move endpoints, Realtime event payload normalized.

Gate: Calendar visibly và measurably faster than old app on D3. Có thể enter Appointment Detail và handoff basic appointment context.
Phase 3 · Owner: FE2 + BE1
Checkout / Basket
10–14 weeks

FE: Service/category/staff selection, Basket domain, Appointment→checkout handoff, Checkout group, Discount/redeem/tip pre-payment state, Customer attach/search basic, Local cache.

BE: Checkout session/basket endpoints, Service/staff/category aggregate APIs, Customer lookup APIs.

Gate: Calendar → Checkout → Calendar roundtrip không lose hoặc corrupt state.
Phase 4 · Owner: FE2 + FE3 + BE1
Payment, Receipt & Printer
12–16 weeks

FE: Payment orchestration, Cash/card/split/tip/redeem rules, Payment result model, Receipt view model, Printer adapter, SUNMI/D3 printer flow, External printer adapter, Retry/reconnect.

BE: Payment status/result APIs, Receipt payload endpoint, Terminal/payment callback contract.

Gate: Payment complete → receipt → print pass trên real D3/hardware, không chỉ emulator.
Phase 5
Caller, Customer & Minimum Settings
8–10 weeks
  • Caller session domain (incoming, active, booked, ended, cancelled)
  • Caller → booking/calendar flow
  • Customer create/search/detail minimum
  • D3 operational settings: printer, payment terminal, staff visibility, business hours
Gate: Một store có thể operate Salon POS core day trên D3 trong staging/pilot.
Phase 6
Pilot & Cutover
6–8 weeks
  • Staging pilot, Store pilot, Defect burn-down
  • Parity report against old app
  • Rollback plan, Production rollout plan
Gate: Không P0/P1 parity gaps remaining cho Calendar, Checkout, Payment, Printer.

§ 06Team Allocation

Frontend 1
Calendar & Realtime UI
  • Calendar surface
  • Projection/layout engine
  • Drag/drop
  • Signal event application to UI
  • D3 performance benchmark
Frontend 2
Checkout & Payment
  • Basket
  • Service/staff/category selection
  • Payment rules
  • Checkout/payment handoff
Frontend 3
Platform, Printer & Shell
  • App shell
  • Navigation/session
  • Room/cache infrastructure
  • Printer/receipt
  • Dialog/workflow system
  • Design system
Backend 1
BFF & API / Realtime Contracts
  • Aggregate APIs
  • Normalized backend responses
  • Payment/receipt contracts
  • Realtime event contracts
  • Compatibility với existing backend

§ 07Estimate

Với 3 FE + 1 BE engineer:

ScopeEstimate
Android D3 Salon core MVP8–10 months
Production-ready Android replacement cho Salon core10–12 months
Broad Android parity với phần lớn old-app behavior14–18 months
Bao gồm Retailer, Reports, deep Settings18+ months

Core MVP includes

Calendar · Appointment Detail · Checkout/Basket · Payment · Receipt/Printer · Caller · Customer basic · Minimum Settings

Core MVP excludes by default

Full Retailer mode · Deep reports · Deep inventory · Full settings parity · Broad marketing flows

§ 08Critical Path

Foundation
-> Calendar
-> Calendar/Checkout handoff
-> Payment
-> Receipt/Printer
-> Pilot
▲ Bottleneck

Backend có khả năng là bottleneck vì chỉ có một backend engineer. Nếu BFF/API contracts lag, frontend sẽ depend trên mocks lâu hơn và parity risk tăng.

§ 09Risks

▽ Risk register
  • Full parity dễ underestimate vì nhiều business rules ẩn trong old UI/hooks/sagas.
  • Checkout/Basket có largest blast radius và có thể lâu hơn Calendar.
  • Printer/payment/caller issues cần real devices, không validate được trong emulator.
  • BFF/API contract delays có thể block frontend integration.
  • Rebuild Calendar thành công không làm product ready trừ khi checkout/payment/receipt cũng smooth.

§ 10Key Decisions

  • Không bao gồm full Retailer/Inventory/Reports trong Android D3 MVP, trừ khi product scope explicitly require.
  • Mang Customer basic sớm hơn normal screen-priority plan, vì overlap nặng với Checkout và modal workflows.
  • Giữ Payment và Printer/Receipt gần nhau trong roadmap; chúng là một operational flow.
  • Calendar phải là first major vertical slice vì là main performance reason cho rewrite.
  • Checkout/Basket phải follow ngay sau vì là largest business-coupling cluster.
Document III · Tooling Guide

Impact Analysis · Hướng dẫn sử dụng

Bộ công cụ phân tích ảnh hưởng của thay đổi code, giúp estimate độ phức tạp và rủi ro trước khi bắt đầu làm việc.

Cài đặt lần đầu

yarn graph:build

Chạy một lần sau khi clone repo, hoặc sau khi có refactor lớn (đổi tên file, di chuyển module). Tạo ra file .impact-graph.jsonkhông commit file này.

Ba lệnh chính

LệnhKết quảDùng khi
yarn graph:buildBuild/rebuild dependency graphLần đầu, hoặc sau refactor lớn
yarn graph:impactIn prompt phân tích ra terminalCopy vào AI để estimate
yarn graph:vizMở HTML visualization trên browserMuốn nhìn trực quan

Cách dùng chi tiết

Phân tích file cụ thể

yarn graph:impact src/redux/slices/salon/calendar/index.js
yarn graph:viz src/redux/slices/salon/calendar/index.js

Phân tích nhiều file cùng lúc

yarn graph:impact src/apis/index.js src/redux/saga/checkout/basket/addBasketItem.js

Auto lấy từ git diff (so với main)

yarn graph:impact    # tự detect các file đã thay đổi
yarn graph:viz       # tương tự, mở browser

Điều chỉnh độ sâu phân tích

yarn graph:impact --depth 2 src/apis/index.js   # ít hơn (nhanh hơn)
yarn graph:impact --depth 4 src/utils/base.js   # sâu hơn (full blast radius)

Mặc định depth=3. Depth cao hơn → thấy nhiều ảnh hưởng gián tiếp hơn.

Copy vào clipboard để paste vào AI

yarn graph:impact | pbcopy
# → paste vào Claude Code, Copilot Chat, Cursor, hoặc bất kỳ AI nào

Đọc kết quả visualization

Màu sắc node

  • 🔴 Đỏ — file bạn đang thay đổi
  • 🟠 Cam — Hop 1: trực tiếp import file đỏ
  • 🟡 Vàng — Hop 2: gián tiếp (import hop 1)
  • 🟢 Xanh — Hop 3: xa hơn

Kích thước node = kích thước file. Node to = file phức tạp (XL).

Click vào node → sidebar hiện

  • Đường dẫn đầy đủ
  • Số lines + complexity label (S/M/L/XL)
  • Danh sách file nó import và ai đang import nó

Search (ô tìm kiếm trên cùng) → filter theo tên file, dim các node không liên quan.

Quy trình theo từng loại công việc

Bug Fix

Mục tiêu: Sửa đúng, không làm hỏng chỗ khác.

# 1. Xác định file cần sửa
yarn graph:viz src/path/to/buggy-file.js

# 2. Xem ai đang dùng file đó (hop 1 = test focus)
# → Những file cam là nơi ưu tiên test sau khi sửa

# 3. Estimate (optional)
yarn graph:impact src/path/to/buggy-file.js | pbcopy
# Paste vào AI: "File này có bao nhiêu caller? Rủi ro khi sửa logic X?"

Khi nào cần rebuild graph: Không cần, trừ khi bug fix đổi tên/di chuyển file.

Small Change / Tweak

Mục tiêu: Biết thay đổi nhỏ này ảnh hưởng bao xa.

# Nhìn nhanh blast radius
yarn graph:impact --depth 2 src/path/to/file.js

# Nếu affected < 20 files → low risk, proceed
# Nếu affected > 100 files → cẩn thận, đọc kỹ các hop 1

New Feature

Mục tiêu: Chọn đúng chỗ đặt code mới, tránh tạo circular deps.

# 1. Xem structure của module sẽ tích hợp vào
yarn graph:viz src/module/bạn-định-dùng/index.js

# 2. Kiểm tra file sẽ import vào feature mới
yarn graph:impact src/shared/hooks/useXxx.js src/redux/slices/salon/index.js

# 3. Paste vào AI để hỏi:
#    "Tôi muốn thêm feature Y vào module này.
#     File nào phù hợp nhất để đặt logic? Có nguy cơ circular deps không?"

Lưu ý: Sau khi tạo file mới và thêm imports, rebuild graph để update: yarn graph:build.

Refactor

Mục tiêu: Biết đầy đủ những gì cần update khi đổi API/interface.

# 1. Phân tích file sẽ refactor
yarn graph:impact --depth 4 src/path/to/file.js | pbcopy

# 2. Paste vào AI với context:
#    "Tôi muốn refactor hàm X trong file này (đổi params/return type).
#     Dựa vào blast radius này, estimate effort và những file nào cần update."

# 3. Mở viz để nhìn toàn cảnh
yarn graph:viz --depth 4 src/path/to/file.js
# → Click lần lượt vào các hop 1 (cam) để hiểu caller pattern

Khi nào rebuild: Sau khi refactor xong (file đổi tên, interface đổi).

Upgrade Dependency / Library

Mục tiêu: Biết bao nhiêu màn hình/component sẽ bị ảnh hưởng.

# Tìm tất cả files liên quan đến library cần upgrade
grep -r "from 'tên-library'" src --include="*.js" -l > /tmp/affected.txt
cat /tmp/affected.txt

# Phân tích impact của các entry points chính
yarn graph:impact $(grep -r "from 'tên-library'" src --include="*.js" -l | head -5 | tr '\n' ' ')

# Hoặc nếu library được wrap qua một file:
yarn graph:viz src/shared/services/api/axiosClient.js   # ví dụ upgrade axios

Ví dụ thực tế:

# Upgrade axios → xem ai dùng axiosClient
yarn graph:viz src/shared/services/api/axiosClient.js

# Upgrade react-navigation → xem ai dùng RootNavigator
yarn graph:viz src/navigators/RootNavigator.js

Performance Optimization

Mục tiêu: Tìm bottleneck, hiểu ripple effect của thay đổi.

# Tìm các hub lớn nhất (nhiều dependents + file XL)
node -e "
const g = require('./.impact-graph.json');
const { reverse, meta } = g;
Object.keys(reverse)
  .map(f => ({ f, deps: reverse[f].length, lines: meta[f]?.lines || 0 }))
  .filter(x => x.deps > 10 && x.lines > 400)
  .sort((a,b) => b.deps - a.deps)
  .slice(0, 15)
  .forEach(x => console.log(x.deps + ' dependents | ' + x.lines + 'L | ' + x.f));
"

# Visualize hub quan trọng nhất
yarn graph:viz src/utils/base.js

Refactor chương trình lớn

Mục tiêu: Re-estimate các phase kiến trúc lớn bằng blast radius thật, tránh lập kế hoạch theo cảm giác.

# Calendar
yarn graph:impact --depth 3 \
  src/redux/slices/salon/calendar/index.js \
  src/salon/SalonHomeScreen/pages/CalendarPage/useProps.js \
  src/salon/SalonHomeScreen/pages/CalendarPage/components/CalendarBody/index.js

# Checkout/Basket
yarn graph:impact --depth 3 \
  src/redux/slices/salon/basketLocal/index.js \
  src/redux/slices/salon/checkoutSelection/index.js \
  src/redux/saga/checkout/selection/refreshCheckoutItems.js

# Signal runtime
yarn graph:impact --depth 3 \
  src/shared/providers/SignalProvider.js \
  src/shared/hooks/signalr/useSignalR.js

# Data layer
yarn graph:impact --depth 3 \
  src/apis/request.js \
  src/apis/useHarmonyQuery.js \
  src/apis/useHarmonyMutation.js \
  src/shared/services/RTKQuery/baseApi/index.js

# Store/app shell - chỉ để đo risk, không phải entry point nên đụng sớm
yarn graph:impact --depth 3 \
  src/redux/store/index.js \
  src/redux/slices/index.js \
  src/redux/reducers/index.js \
  src/redux/saga/index.js

Mốc tham chiếu hiện tại của repo

Tại thời điểm build graph gần nhất:

  • 2,470 JS/TS files
  • 9,445 dependency edges

Blast radius tham chiếu

CụmAffected filesNhận định
Calendar344Rất cao
Checkout/Basket472Cao nhất trong domain core
Payment343Cao
Printer/Receipt248Cao
Signal runtime26Thấp hơn tưởng tượng, phù hợp làm enabler sớm
Data layer35Vừa, nhưng có tính nền tảng
Store/state root610Rất cao, không nên refactor trực diện sớm
Modal/app shell494Rất cao, cần tránh đụng đầu kỳ

Rule of thumb cho estimate

Affected filesNature
< 20Low-risk local change
20–100Medium-risk, cần test focus rõ
100–300High-risk domain refactor
300+Program-level change, cần flag/parity gate/rollout riêng
500+Gần như systemic change, tránh dùng làm entry point cho phase đầu

Đọc complexity label

LabelLinesÝ nghĩa
S< 100Nhỏ, ít rủi ro
M100–299Trung bình
L300–599Lớn, cẩn thận khi sửa
XL≥ 600Phức tạp cao, cần review kỹ

Câu hỏi gợi ý khi paste vào AI

Sau khi copy output của yarn graph:impact | pbcopy, paste vào AI cùng một trong các câu hỏi sau:

Estimate effort

"Dựa vào blast radius này, estimate dev hours để [thay đổi X].
 Tính cả code + test + review."

Risk assessment

"File nào trong hop 1 có rủi ro cao nhất nếu tôi thay đổi [interface Y]?
 Tại sao?"

Test planning

"Tôi cần test những gì sau khi thay đổi các file này?
 Ưu tiên theo risk."

Architecture advice

"Có pattern nào tốt hơn để tránh blast radius lớn như thế này không?"

Khi nào cần rebuild graph

yarn graph:build   # chạy lại khi:
  • ✅ Clone repo lần đầu
  • ✅ Merge branch lớn (nhiều file mới/đổi tên)
  • ✅ Refactor xong (di chuyển/đổi tên file)
  • ✅ Thêm alias mới trong babel.config.js
  • ❌ Không cần sau mỗi commit thông thường
  • ❌ Không cần khi chỉ thay đổi logic bên trong file (không thêm/bớt import)
Document IV · Plan Template

Plan · Refactor Calendar Slice

Template plan chi tiết, đã pre-fill phần Impact Analysis cho lần refactor calendar slice tiếp theo. Dùng làm tham chiếu hoặc copy ra ticket riêng.
Ngày tạo2026-05-19
Trạng thái🔲 Draft
Assignee
Ticket/PR

1 · Mô tả

Loại công việc: Bug Fix / New Feature / Refactor / Upgrade / Performance

Vấn đề hiện tại: (điền)

Giải pháp đề xuất: (điền)

2 · Files thay đổi (seeds)

  • src/redux/slices/salon/calendar/index.js — 1,523 lines XL

3 · Impact Analysis

Generated by yarn graph:impact — depth 3

Blast radius343 files bị ảnh hưởng gián tiếp
Risk level🔴 Very High
Effort hint (code + test + review)22–48h

Hop 1 — 1 file

FileLinesSizeDependents
src/redux/slices/index.js61S164

Hop 2 — 164 files

FileLinesSizeDependents
src/salon/SalonHomeScreen/pages/CalendarPage/components/AppointmentDetailModal/useProps.js1,762XL1
src/retailer/RTHomeScreen/pages/CheckOutPage/useProps/usePaymentPageProps.js1,675XL1
src/retailer/RTHomeScreen/pages/HomeOrderPayPage/useProps.js1,624XL2
src/retailer/RTHomeScreen/pages/HomeOrderCheckOutPage/useProps.js950XL2
src/retailer/RTHomeScreen/pages/CheckOutPage/useProps/useProps.js787XL3
src/shared/components/Receipts/ReceiptSettlementModal.js781XL1
src/merchant/SettingScreen/pages/SettingPage/components/tabsRedesign/SalonTabs/Controllers/useController.js757XL5
src/salon/SalonHomeScreen/pages/CalendarPage/components/AppointmentDetailModal/CallerIdBookingModal.js756XL1
src/shared/hooks/printer/usePrinterManager.js755XL31
src/shared/hooks/flows/arisePayment/usePaymentAriseTerminalFlow.js725XL2
src/merchant/SettingScreen/pages/SettingPage/components/tabs/AdvanceTab/useProps.js657XL1
src/screens/TabMarketing/widget/TabPromotion/salon/useMainProps.js621XL4
src/retailer/RTHomeScreen/widget/FormAddressInformation.js566L0
src/salon/SalonHomeScreen/pages/CalendarPage/components/CalendarBody/Columns/WaitingColumn.js562L1
src/salon/SalonHomeScreen/pages/CalendarPage/components/CalendarHeader/index.js527L1
… và 149 file nhỏ hơn

Hop 3 — 178 files

FileLinesSizeDependents
src/components/modals/RecurringSchedule/index.js1,683XL1
src/screens/InvoiceScreen/index.js1,417XL2
src/salon/SalonHomeScreen/pages/CalendarPage/components/AppointmentDetailModal/Layout.js1,111XL2
src/screens/InvoiceScreen/useProps.js898XL1
src/salon/SalonHomeScreen/components/checkouts/PaymentCompletePanel.js678XL1
src/retailer/RTHomeScreen/widget/BasketPaymentContent.js664XL0
src/shared/components/Receipts/PaymentCompleteReceipt.js580L1
src/utils/receiptXMLDejavoo.js533L0
src/shared/components/Receipts/PopupReceipt.js512L0
src/shared/components/DialogReturnItemComplete.js511L0
src/screens/SettingScreen/widget/TabHardware/pages/SetupHardware/useProps.js491L1
src/shared/components/Receipts/ReceiptModal.js462L1
src/shared/components/Settings/ReminderSetting/Table/TableList.js461L1
src/shared/providers/ModalProvider.js452L183
src/screens/SettingScreen/widget/TabHardware/widget/PrinterList_old.js429L0
… và 163 file nhỏ hơn

4 · Estimate

Hạng mụcEstimate
Viết code___h
Unit / manual test___h
Code review___h
Buffer (unexpected)___h
Tổng___h

Complexity: S / M / L / XL
Confidence: High / Medium / Low — vì sao?

5 · Kế hoạch thực hiện

  • ☐ Bước 1 …
  • ☐ Bước 2 …
  • ☐ Bước 3 …

6 · Test checklist

Dựa vào hop 1 (1 file trực tiếp bị ảnh hưởng):

  • ☐ Kiểm tra index
  • ☐ Regression: màn hình nào cần smoke test
  • ☐ Edge cases: liệt kê

7 · Rollout / Risk mitigation

Feature flag? Staged rollout? Cần monitor gì sau deploy?

  • ☐ …

8 · Notes

Ghi chú thêm, quyết định, trade-offs.