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.
Document Index
| 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 |
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.
Calendar Performance and Architecture Report
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/CalendarPage và src/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:
c9abb28devào 2023-10-02, messageadd 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:
- Calendar
- Checkout
- Printer
- Payment
- 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
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
| File | Lines (approx) |
|---|---|
| CalendarHeader/index.js | 526 |
| CalendarBody/index.js | 401 |
| useScrollSync.js | 615 |
| useHandleAfterDroppingBlock.js | 513 |
| useHandleSignalCalendar.js | 360 |
| calendar slice index.js | 1,522 |
| calendar selectors.js | 277 |
Tín hiệu khác:
- Số
useSelectortrong Calendar subtree: ~60. - Số
JSON.stringifyusages trong Calendar subtree: ~16. FlashListtập trung ởuseScrollSync.js,CalendarBody/index.js, vàCalendarHeader/index.js.Canvasusage trongcomponents/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:
setCalendarDataByDateremoveStaffupdateBlocksBookingOnlineupdateTurnForStaffscancelAppointmentmoveAppointmentToWaitingupdateAppointmentFromSignaladdAppointmentupdateBlockTimes
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 feature3751afdb7: Refine iOS calendar smooth scroll stability implementation26f5a79e8: Enhance iOS smooth scrolling behavior in CalendarPagee39352391: 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ụ:
CalendarHeaderStaffItemcompareprevProps.itemvànextProps.itembằngJSON.stringify.PanGestureContainerdùng stringified reset params.useCalendarVerticalScrolldùngJSON.stringify(workingTimes)trong dependencies.useHandleSignalCalendarcũ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.CALENDAR và CalendarBody cũng dùng raster/texture flags như:
shouldRasterizeIOSrenderToHardwareTextureAndroid
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
Work trước đó không random. Các quyết định tốt:
- Dùng
FlashListthay vì plainFlatListcho 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.runAfterInteractionstrong 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
CalendarPagevàredux/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
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.jsvàAppointmentDetailModal/useProps.js.
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.
Isolate payment business rules khỏi checkout UI.
Actions- Group payment rules quanh hooks như
usePayBasket.jsvàusePaymentZCP.js. - Tách split payment, tip, redeem, cash permission, transaction fee.
- Define payment result model dùng cho checkout/receipt/calendar.
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.
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:
- staff
- appointment
- block time
- waiting
- any-staff
- business hours
- overlap
- SignalR updates
- API responses
- local optimistic state
- conflict handling
- batch/merge behavior
- 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
selectStaffByIdselectBlockIdsByStaffIdselectLayoutByStaffId
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.
- í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
- 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.
- 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
- 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
| File | Dependents |
|---|---|
| src/shared/themes/index.js | 817 |
| src/utils/index.js | 813 |
| src/resources/index.js | 499 |
| src/shared/providers/ModalProvider.js | 183 |
| src/redux/slices/index.js | 164 |
| src/apis/index.js | 155 |
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.
- 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
- Normalize Calendar state và projection flow
- Calendar event queue / coalescing
- Simplify Android scroll authority
- Giữ Ticket View, Caller ID, Checkout handoff parity
- 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
- Define payment result contract
- Isolate payment rules khỏi checkout widgets
- Align checkout, payment, receipt trên một shared model
- 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
- 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
| Scope | Team | Estimate |
|---|---|---|
| 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
| Workstream | Estimate |
|---|---|
| Foundation + governance + tests + baseline | 3–5 weeks |
| Calendar refactor | 8–12 weeks |
| Signal runtime / event queue | 2–4 weeks |
| Checkout / Basket refactor | 10–14 weeks |
| Payment result / rules boundary | 6–8 weeks |
| Printer / Receipt adapter boundary | 8–10 weeks |
| Caller domain extraction | 4–6 weeks |
| Data-layer modernization across core domains | 10–16 weeks, parallel by domain |
| Shared-layer cleanup | 8–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
- Giữ
.impact-graph.jsonlocal. Rebuild sau major refactors. Không commit file này. - Bắt đầu với low-blast-radius enablers: Signal event queue, feature flags, baseline harness, domain adapters.
- Tránh early direct changes vào:
redux/store/index.jsredux/slices/index.jsModalProvider.jsRootNavigator.js
- Dùng impact analysis trước mỗi phase lớn để confirm blast radius chưa mở rộng bất ngờ.
- 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
| Term | Meaning |
|---|---|
| Domain state | Source-of-truth business data: staff, appointments, block times, waiting list, working hours. |
| Projection / render model | Derived data cần để draw Calendar: block positions, column widths, overlap groups, header badges. |
| Incremental layout | Recomputing chỉ staff/columns affected by a change, thay vì recalculating whole day. |
| Scroll authority | Single component/system own scroll position trong một gesture. |
| Coalescing | Gom nhiều realtime updates thành một UI update, để interface không react theo từng packet. |
Android D3 Native Rewrite Impact Plan
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 |
|---|---|---|
| Calendar | 344 files / ~71k lines | Không thể rebuild như isolated screen; links to AppointmentDetail, Ticket, Checkout, Receipt, Caller. |
| Checkout/Basket | 472 files / ~110k lines | Largest core business-rule cluster. Phải treat như major domain, không phải panel. |
| Payment | 343 files / ~71k lines | Coupling chặt với Checkout, Receipt, Printer, device flows. |
| Printer/Receipt | 248 files / ~75k lines | Hardware và receipt behavior carry high operational risk. |
| Customer | 206 files / ~64k lines | Customer cần sớm vì booking, checkout, modal flows phụ thuộc. |
| Modal/Shell | 494 files / ~120k lines | Current app dùng global modal như một workflow framework. New app cần explicit flow orchestration. |
| Signal runtime | 26 files / ~6k lines | Blast radius nhỏ hơn dự kiến; có thể redesign early và cleanly. |
| Data layer | 35 files / ~9.7k lines | Graph radius modest, nhưng BFF/API contract risk vẫn high. |
| Inventory | 30 files / ~6.4k lines | Có thể defer nếu Android D3 v1 là Salon POS core. |
| Retailer | 15 files / ~4.5k lines | Không nên trong MVP trừ khi D3 explicitly require Retailer mode. |
Important overlaps
| Cluster pair | Overlap (files) |
|---|---|
| Calendar & Checkout | 340 |
| Calendar & Payment | 340 |
| Checkout & Printer | 236 |
| Checkout & Customer | 200 |
| Printer & Customer | 191 |
| Modal/Shell & Checkout | 331 |
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
- 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.
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.
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.
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.
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.
- 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
- Staging pilot, Store pilot, Defect burn-down
- Parity report against old app
- Rollback plan, Production rollout plan
§ 06Team Allocation
- Calendar surface
- Projection/layout engine
- Drag/drop
- Signal event application to UI
- D3 performance benchmark
- Basket
- Service/staff/category selection
- Payment rules
- Checkout/payment handoff
- App shell
- Navigation/session
- Room/cache infrastructure
- Printer/receipt
- Dialog/workflow system
- Design system
- Aggregate APIs
- Normalized backend responses
- Payment/receipt contracts
- Realtime event contracts
- Compatibility với existing backend
§ 07Estimate
Với 3 FE + 1 BE engineer:
| Scope | Estimate |
|---|---|
| Android D3 Salon core MVP | 8–10 months |
| Production-ready Android replacement cho Salon core | 10–12 months |
| Broad Android parity với phần lớn old-app behavior | 14–18 months |
| Bao gồm Retailer, Reports, deep Settings | 18+ 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
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
- 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.
Impact Analysis · Hướng dẫn sử dụng
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.json — không commit file này.
Ba lệnh chính
| Lệnh | Kết quả | Dùng khi |
|---|---|---|
| yarn graph:build | Build/rebuild dependency graph | Lần đầu, hoặc sau refactor lớn |
| yarn graph:impact | In prompt phân tích ra terminal | Copy vào AI để estimate |
| yarn graph:viz | Mở HTML visualization trên browser | Muố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ụm | Affected files | Nhận định |
|---|---|---|
| Calendar | 344 | Rất cao |
| Checkout/Basket | 472 | Cao nhất trong domain core |
| Payment | 343 | Cao |
| Printer/Receipt | 248 | Cao |
| Signal runtime | 26 | Thấp hơn tưởng tượng, phù hợp làm enabler sớm |
| Data layer | 35 | Vừa, nhưng có tính nền tảng |
| Store/state root | 610 | Rất cao, không nên refactor trực diện sớm |
| Modal/app shell | 494 | Rất cao, cần tránh đụng đầu kỳ |
Rule of thumb cho estimate
| Affected files | Nature |
|---|---|
| < 20 | Low-risk local change |
| 20–100 | Medium-risk, cần test focus rõ |
| 100–300 | High-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
| Label | Lines | Ý nghĩa |
|---|---|---|
| S | < 100 | Nhỏ, ít rủi ro |
| M | 100–299 | Trung bình |
| L | 300–599 | Lớn, cẩn thận khi sửa |
| XL | ≥ 600 | Phứ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)
Plan · Refactor Calendar Slice
| Ngày tạo | 2026-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 radius | 343 files bị ảnh hưởng gián tiếp |
| Risk level | 🔴 Very High |
| Effort hint (code + test + review) | 22–48h |
Hop 1 — 1 file
| File | Lines | Size | Dependents |
|---|---|---|---|
| src/redux/slices/index.js | 61 | S | 164 |
Hop 2 — 164 files
| File | Lines | Size | Dependents |
|---|---|---|---|
| src/salon/SalonHomeScreen/pages/CalendarPage/components/AppointmentDetailModal/useProps.js | 1,762 | XL | 1 |
| src/retailer/RTHomeScreen/pages/CheckOutPage/useProps/usePaymentPageProps.js | 1,675 | XL | 1 |
| src/retailer/RTHomeScreen/pages/HomeOrderPayPage/useProps.js | 1,624 | XL | 2 |
| src/retailer/RTHomeScreen/pages/HomeOrderCheckOutPage/useProps.js | 950 | XL | 2 |
| src/retailer/RTHomeScreen/pages/CheckOutPage/useProps/useProps.js | 787 | XL | 3 |
| src/shared/components/Receipts/ReceiptSettlementModal.js | 781 | XL | 1 |
| src/merchant/SettingScreen/pages/SettingPage/components/tabsRedesign/SalonTabs/Controllers/useController.js | 757 | XL | 5 |
| src/salon/SalonHomeScreen/pages/CalendarPage/components/AppointmentDetailModal/CallerIdBookingModal.js | 756 | XL | 1 |
| src/shared/hooks/printer/usePrinterManager.js | 755 | XL | 31 |
| src/shared/hooks/flows/arisePayment/usePaymentAriseTerminalFlow.js | 725 | XL | 2 |
| src/merchant/SettingScreen/pages/SettingPage/components/tabs/AdvanceTab/useProps.js | 657 | XL | 1 |
| src/screens/TabMarketing/widget/TabPromotion/salon/useMainProps.js | 621 | XL | 4 |
| src/retailer/RTHomeScreen/widget/FormAddressInformation.js | 566 | L | 0 |
| src/salon/SalonHomeScreen/pages/CalendarPage/components/CalendarBody/Columns/WaitingColumn.js | 562 | L | 1 |
| src/salon/SalonHomeScreen/pages/CalendarPage/components/CalendarHeader/index.js | 527 | L | 1 |
| … và 149 file nhỏ hơn | |||
Hop 3 — 178 files
| File | Lines | Size | Dependents |
|---|---|---|---|
| src/components/modals/RecurringSchedule/index.js | 1,683 | XL | 1 |
| src/screens/InvoiceScreen/index.js | 1,417 | XL | 2 |
| src/salon/SalonHomeScreen/pages/CalendarPage/components/AppointmentDetailModal/Layout.js | 1,111 | XL | 2 |
| src/screens/InvoiceScreen/useProps.js | 898 | XL | 1 |
| src/salon/SalonHomeScreen/components/checkouts/PaymentCompletePanel.js | 678 | XL | 1 |
| src/retailer/RTHomeScreen/widget/BasketPaymentContent.js | 664 | XL | 0 |
| src/shared/components/Receipts/PaymentCompleteReceipt.js | 580 | L | 1 |
| src/utils/receiptXMLDejavoo.js | 533 | L | 0 |
| src/shared/components/Receipts/PopupReceipt.js | 512 | L | 0 |
| src/shared/components/DialogReturnItemComplete.js | 511 | L | 0 |
| src/screens/SettingScreen/widget/TabHardware/pages/SetupHardware/useProps.js | 491 | L | 1 |
| src/shared/components/Receipts/ReceiptModal.js | 462 | L | 1 |
| src/shared/components/Settings/ReminderSetting/Table/TableList.js | 461 | L | 1 |
| src/shared/providers/ModalProvider.js | 452 | L | 183 |
| src/screens/SettingScreen/widget/TabHardware/widget/PrinterList_old.js | 429 | L | 0 |
| … và 163 file nhỏ hơn | |||
4 · Estimate
| Hạng mục | Estimate |
|---|---|
| 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.