How it's built · Technical blueprint

The engine behind every roll.

Maki Taki isn't getting a brochure website. It's getting a direct-to-customer growth platform: branch-aware ordering, loyalty, gamification, referrals, promotions, CRM and a full admin portal, all running on one clean, auditable backend that your team controls without code deployments.

Architecture principle

一One customer identity 二One ordering platform 三One loyalty ledger 四Branch-aware configuration 五Event-driven marketing
24domain modules
30+core data entities
/api/v1REST + OpenAPI
99.9%availability target for ordering
循環02

The growth loop

Every feature earns its place in the loop.

The product revolves around one cycle: Discover → Order → Earn → Engage → Reward → Return → Refer. Each stage is driven by specific features and moves a measurable number. Nothing is built just because it looks nice.

設計03

System architecture

One backend. Every channel.

The website, the admin portal and any future app or POS all talk to the same versioned API. Business rules live in one trusted place. Slow work runs in background workers, and every outside provider sits behind a swappable adapter.

CLIENTS EXTERNAL PROVIDERS DATA WORKERS · BULLMQ ANALYTICS · OPTIONAL HTTPS transactions cache · locks media enqueue jobs send via adapters queues events Customer Web / PWANext.js · SSR · TSinstallable PWA Admin PortalNext.js / Reactshared UI + types Future channelsMobile apps · POSWhatsApp · deliverysame /api/v1 CDN · WAF TLS · caching · filtering API · NestJS + TypeScript /api/v1 · REST · OpenAPI Auth · RBAC · validation · rate limits · idempotency · audit Domain events · order.delivered → points · tokens · triggers Stateless nodes · horizontally scalable · Docker containers Paymentscard gateway · wallet · cashsigned, idempotent webhooks SMS · WhatsApp · Emailapproved providerstemplates · delivery status Maps & Geocodingaddress → zone → branchpolygon delivery zones each behind an adapter interface PostgreSQLorders · ledgeraudit · eventsfull-text search Rediscachelocks · capsjob queues S3 storageimages · mediaWebP / AVIFserved via CDN campaigns & automations points & rewards notifications scheduled · retries · dead-letter Warehouse / ClickHouseadded as volume growscohorts · BI · long-rangeday one: events in DBand queue OPERATIONSStructured logs · metrics · tracing · error reporting · correlation IDs · alerting · automated backups with tested restore
i
Hover or tap any block

Each part has one job. The arrows show who talks to whom. Solid arrows are live requests, and dashed arrows are optional or future paths.

← Swipe the diagram sideways to see all of it →

Branch and fulfilment model

Branch-aware by design

Every order belongs to exactly one fulfilment branch. Prices, stock, fees, minimum order, delivery eligibility and promotions are all evaluated against that branch before payment.

1Address2Eligible branches3Selected branch4Current menu & pricing5Payment
Menu visibilityGlobal + branch override
PriceGlobal + branch override
AvailabilityBranch
Working hoursBranch + fulfilment type
Delivery zonesBranch (polygons / areas)
Min. order / feeBranch / zone
Preparation timeBranch, optionally dynamic
PromotionsGlobal, selected or excluded
LoyaltyGlobal, with exclusions
Tax / service feeBranch or jurisdiction
道具04

Tech stack

Proven tools, chosen for the long run.

Mainstream, well-supported technology that any competent team can maintain. No exotic bets and no lock-in to a single vendor. Here's what each piece is and why it matters to the business.

構成05

Domain modules & code layout

24 focused modules, one tidy monorepo.

The backend is split into small domain modules. Each owns its rules, data and tests. Web, admin, API and workers live in one repository and share types, so a change to an order or a promotion stays consistent everywhere.

maki-taki/monorepo
maki-taki/
├─ apps/
│  ├─ web/          # Customer website / PWA
│  ├─ admin/        # Admin portal
│  ├─ api/          # Backend API
│  └─ worker/       # Queues, campaigns, scheduled jobs
├─ packages/
│  ├─ ui/           # Shared UI / design system
│  ├─ contracts/    # DTOs, schemas, generated API types
│  ├─ domain/       # Shared domain rules
│  ├─ config/       # Lint / TS / config
│  └─ testing/      # Fixtures and test helpers
├─ infra/           # Docker / IaC / deployment
└─ docs/            # Architecture, ADRs, API docs
資料06

Data model

A data model that remembers everything.

One customer record connects orders, points, rewards, games and referrals. Orders keep a frozen snapshot of what was bought. Points live in a ledger, not a single editable number. Hover any entity to see its key fields.

← Swipe the diagram sideways to see all of it →

Snapshots, not references

An OrderItem stores the product, price and modifiers as they were at the time of sale. Editing the menu tomorrow never rewrites yesterday's orders.

Ledgers, not counters

Points are the sum of append-only LoyaltyLedger rows. The balance on LoyaltyAccount is only a cache and can always be rebuilt.

Archive, don't delete

Business records are soft-deleted or archived. Historical orders, ledgers and the AuditLog are immutable.

流れ07

How the critical flows work

Where money and rewards move, the server decides.

These four flows carry the commercial risk: orders, pricing, points and prizes. Step through each one to see how it stays correct even with double taps, retried payments or someone tampering with the browser.

Order lifecycle

Every order moves through explicit states. Every change is timestamped and appended to an immutable status history, so you can always answer "what happened, when, and who did it".

History is append-only
    order_status_history · #MT-1057immutable

      Checkout quote

      The browser sends only a cart and the customer's choices. The server recalculates branch, prices, fees, promotions and points. Placing the order carries an idempotency key, so a double tap or a network retry can't create two orders.

      The client is never trusted

        The loyalty ledger

        Points are never stored as one editable number. Every change is a new ledger row with its amount, reason, source and expiry. The balance is derived from those rows, which makes points auditable, reversible and impossible to spend twice.

        Append-only · derived balance
        #WhenTypePointsSourceExpiry
        Ledger balance
        0
        Reserved at checkout0
        Available to redeem0
        balance = Σ ledger.points
        cached on LoyaltyAccount · rebuildable

          Lucky Roll

          A qualifying action grants a play token. Playing consumes it atomically, then the server picks a prize from a weighted pool, enforces stock and caps, and writes an audit record. The browser only plays the animation.

          The browser never decides the prize
          Browser
          Lucky Roll

          1 token available

          Server · gamification
          Token tk_8f2a—
          Audit · GamePlay record
            販促08

            Promotion engine

            Promotions are rules, not price edits.

            Marketing builds offers from ten rule areas, with no developer and no hard-coded discounts. Every rule is stored as validated, versioned data, and the same engine checks it in the cart and again at checkout, so margins stay protected.

            promotion.jsonDSL · v1
            {
              "conditions": {
                "all": [
                  {"field":"branch_id","op":"in","value":["BR01","BR02"]},
                  {"field":"cart.subtotal","op":"gte","value":25},
                  {"field":"customer.orders_90d","op":"gte","value":1}
                ]
              },
              "benefit": {"type":"free_product","product_id":"EDAMAME01","qty":1},
              "limits": {"per_customer":1,"total":500},
              "stacking": {"mode":"exclusive"}
            }
            In plain words: "At branches BR01 and BR02, a returning customer (at least 1 order in 90 days) with a cart of 25 or more gets a free edamame, once per customer, up to 500 times, and it doesn't combine with other offers."

            Safety: the API validates the allowed fields and operators and evaluates them in a controlled engine. It never runs arbitrary code from the database.

            Margin protection

            Guardrails built in

            Minimum subtotal after exclusionsMaximum discount amountExcluded low-margin productsRequired product / categoryOne benefit per customer / householdBudget & redemption capsHigh-value approval workflow · Phase 2

            Referrals ride the same rails

            Rewarded only for real orders

            Attribution survives landing → registration → first qualifying order. The referrer's reward is issued only after the friend's first qualifying paid or delivered order, with self-referral and duplicate-account controls.

            CLICKED→SIGNED_UP→QUALIFIED_ORDER→REWARDEDREJECTED
            接続09

            API surface

            A clean, versioned front door.

            Versioned REST endpoints under /api/v1, documented with OpenAPI. The website uses them today. A mobile app, POS, delivery system or WhatsApp bot can use the very same ones tomorrow. GraphQL is optional and not needed for the MVP.

            GETread POSTcreate / command PATCHupdate CRUDadmin HOOKinbound webhook

            API requirements

              安全10

              Security, privacy & reliability

              Safe with money, data and trust.

              These rules are enforced in the backend and covered by automated tests, including concurrency tests for the last coupon, points double-spend, prize inventory and duplicate payment webhooks.

              Critical business rules
              Security & privacy controls
              Non-functional targets
              分析11

              Analytics & success framework

              Judged by behaviour, not feature count.

              Analytics are first-class events, not just database reports. Every meaningful action is recorded with its source, so we can prove which promotions, games and campaigns actually bring orders. We set a pre-launch baseline and compare cohorts after launch.

              Event taxonomy · minimum events

              Core KPIs

                Primary business outcomes

                Direct-order conversionRepeat-order ratePurchase frequencyAverage order valueReferred first ordersCustomer lifetime revenue

                Guardrails

                Discount / reward cost as % of attributed revenueUnsubscribe / complaint ratesCancellation / refund rateFulfilment qualityMargin

                Attribution

                Source, medium, campaign, content, referral code and landing page are kept in an attribution session. Orders link to the last-touch campaign by default, and raw events are kept for other models later.

                The closed loop

                一

                Customer behaviour generates data

                二

                Segmentation selects relevant audiences

                三

                Controlled promotions & rewards create engagement

                四

                Orders produce measurable revenue

                五

                Analytics show what to continue, stop or improve

                and back again
                範囲12

                MVP scope

                Commercially usable, not a prototype.

                The first release already takes real orders, runs loyalty and promotions, and reports results. The more advanced versions of each capability come later, once real data shows where they pay off.

                In the MVP Launch
                Later Deferred

                Non-goals for MVP

                • A full accounting / ERP system
                • Replacing a sophisticated restaurant POS
                • An independent delivery-driver marketplace
                • Native iOS / Android apps (the web app installs as a PWA)

                How we'll know the MVP is done · acceptance criteria

                  道程13

                  Delivery roadmap

                  Six phases, each with a clear outcome.

                  We build the safe foundation first, then direct ordering, then the growth engine on top. Each phase ends in something the business can use and measure.

                  Durations are set together once the open decisions below are answered.

                  決定14

                  Open decisions · What we need from you

                  Twelve answers before production.

                  These are business decisions, not technical ones, so they're yours to make. Tick them off as we agree on them. Each answer is recorded as configuration or an architecture decision record.

                  0 of 12 answered
                  Your ticks are saved in this browser only.
                  始めましょう

                  Ready to roll?

                  See the platform from both sides: the ordering experience your customers will love, and the portal your team will run it from.

                  Maki Taki · Concept mockupOverviewCustomer siteAdmin