Partner Nook · Developer Reference

Diagrams for engineers — ERD, DFD & the request sequence

Real drawn diagrams, not paragraphs: how the tables relate, how data flows through the system, and the exact call sequence of the affiliate loop.

01 System architecture

Monorepo: Next.js web + NestJS API + PostgreSQL (via Prisma), calling out to Shopify's Admin API.

BrowserAdmin · Portal · Public Web · Next.js 16React 19 · Tailwind v4:3000 API · NestJS 11REST under /api:4000 Prisma 6ORM layer PostgreSQLpartner_nook Shopify APIGraphQL · shpat_ SQL HTTPS

The API owns 4 controllers (admin, affiliates, shopify, tracking) + 2 services (core.ts commission engine, shopify.service.ts Admin-API client).

02 ERD — ownership (multi-tenant)

One Merchant (the brand) owns every table through a merchantId foreign key. Crow's-foot = the "many" side.

Merchant PK id · UQ shop the brand · owns everything ↓ AffiliateFK merchantId CommissionGroupFK merchantId ProductCommissionFK merchantId SignupFieldDefFK merchantId AssetFK merchantId ReferralOrderFK merchantId PayoutFK merchantId FraudFlagFK merchantId EmailLogFK merchantId

Delete a Merchant → all 9 tables cascade-delete. (Click has no merchantId — it lives under Affiliate only; see the next diagram.)

03 ERD — the operational core

The transactional heart: Affiliate and its records, with primary keys, foreign keys and cardinality. Dashed lines are accounting links.

hub entity PK primary key FK foreign key settles / accounting
MerchantPK id · owns all tables Affiliate PK id FK merchantId FK groupId? status · couponCode commissionType/Value balance · signupData CommissionGroup PK id · FK merchantId type · value · tiers(JSON) signupBonus · target Click PK id · FK affiliateId ip · trackierClickId FraudFlag PK id · FK affiliateId? FK orderId? reason · ip · resolved ReferralOrder PK id · FK affiliateId FK merchantId · FK payoutId? shopifyOrderId · commission Payout PK id · FK affiliateId amount · method · status reference · paidAt 1 · N groupId payoutId

Reading it: a Merchant has many Affiliates; a CommissionGroup has many Affiliates (groupId); an Affiliate has many Orders, Payouts, Clicks and FraudFlags; a Payout settles many Orders; an Order can raise FraudFlags.

04 Relationships — exact reference

Every FK with cardinality and on-delete behaviour, straight from schema.prisma.

ParentChildCard.FKOn delete
MerchantAffiliate · CommissionGroup · ReferralOrder · Payout · ProductCommission · SignupFieldDef · Asset · EmailLog · FraudFlag1 : NmerchantIdCascade
CommissionGroupAffiliate1 : NgroupIdoptional (nullable)
AffiliateReferralOrder1 : NaffiliateIdCascade
AffiliateClick1 : NaffiliateIdCascade
AffiliatePayout1 : NaffiliateIdCascade
AffiliateFraudFlag1 : NaffiliateIdSetNull
PayoutReferralOrder1 : NpayoutIdSetNull
ReferralOrderFraudFlag1 : NorderIdSetNull

05 DFD — context (level 0)

The whole system as one process, with the three external entities and the data flowing in and out.

Partner Nook affiliate platform (web + API + DB) Affiliatecreator Merchant / Adminthe brand ShopifyAdmin API signup · view earnings · request payout link · coupon · stats approve · commissions · coupons · payouts push discountssync orders

Two people (creator, brand) and one machine (Shopify) interact with the platform. Everything else is internal.

06 DFD — level 1 (processes & stores)

The five processes in order, each with the ▤ data stores (tables) it writes.

1Registrationsignup form → account 2Approval & couponactivate · generate code · bonus 3Push coupon → Shopifycreate real discount code 4Sync & attributeorder → affiliate → commission 5Payoutrequest → mark paid → credit ▤ Affiliate · SignupFieldDef(r) ▤ Affiliate · EmailLog · Merchant(r) → Shopify discount ▤ ReferralOrder · Affiliate.balance▤ FraudFlag · ProductCommission(r) ▤ Payout · Affiliate · ReferralOrder Affiliate Admin Admin Admin/Shopify Affiliate/Admin

Left = who triggers it · centre = the process · right = which tables change. (r) means read-only.

07 Sequence — the affiliate loop

Call order across the five participants. Solid = request, dashed = response/effect.

Affiliate/Admin Web API DB Shopify ① signup form POST /affiliates/signup INSERT Affiliate ② Admin: approve UPDATE Affiliate · INSERT EmailLog ③ push coupons discountCodeBasicCreate ④ shopper checkout (in Shopify) ⑤ sync orders read orders orders + codes INSERT ReferralOrder · UPDATE balance ⑥ request payout INSERT Payout (REQUESTED) ⑦ Admin: mark paid issue store-credit coupon · UPDATE balance=0

Steps ①–③ onboard & arm the affiliate; ④–⑤ capture & attribute the sale; ⑥–⑦ pay them out.

08 Commission engine — resolution order

calcCommission() in core.ts + a product-level override during sync. First match wins, top to bottom.

Rate resolution (per order)
  1. Personal coupon? → commission = 0
  2. Product / tag rule matches a line item → that rate (affiliate rule > "all")
  3. Per-affiliate rate set → use it
  4. Group rate, tiered by sales-this-month
  5. Merchant default (fallback)
Base & amount
  • Base = subtotal or total (commissionBase)
  • PERCENT: base × value ÷ 100 · FLAT: fixed
  • Tiers: Group.tiers = [{minSales,maxSales,rate}]
  • Writes ReferralOrder.commission, adds to Affiliate.balance
Refund: POST /api/admin/sales/:id/refund subtracts the commission & marks the order REFUNDED. Real Shopify refunds aren't auto-synced yet (webhooks item).

09 API reference

All routes prefixed with /api, grouped by controller.

ControllerEndpoints
trackingGET /r/:code · POST /checkout/simulate
affiliatesPOST signup · POST login · GET :id · POST :id/{profile,payment,referral-code,notifications,password,payout-request}
shopifyGET status · POST push-coupons · POST sync-orders
admin · dashboardGET dashboard · analytics · leaderboard
admin · affiliatesGET affiliates · GET/POST/PUT/DELETE affiliates/:id · POST :id/{approve,reject,mark-paid} · bulk-approve
admin · commissionGET/POST/PUT/DELETE groups · GET/POST/DELETE product-commissions
admin · couponsGET coupons · coupons/assignable · POST/PUT/DELETE coupons · coupons/bulk-update · GET/PUT auto-coupon
admin · salesGET sales · POST sales/:id/{approve,reject,refund}
admin · payoutsGET payouts · payouts/:id/invoice · GET/PUT payment-settings
admin · settingsGET/PUT settings · branding · notifications · signup-fields · signup-defaults
admin · otherGET fraud · POST fraud/:id/resolve · GET/POST/DELETE assets · GET emails · POST emails/bulk

10 Repo structure & run

partnernook/
├─ apps/api/                    # NestJS 11 + Prisma 6
│  ├─ prisma/
│  │  └─ schema.prisma          # 11 models
│  ├─ .env                       # DB url + Shopify token
│  └─ src/
│     ├─ main.ts                 # bootstrap · :4000 · /api
│     ├─ app.module.ts
│     ├─ prisma.service.ts
│     ├─ admin.controller.ts     # brand CRM (largest)
│     ├─ affiliates.controller.ts # portal + signup
│     ├─ shopify.controller.ts   # status/push/sync
│     ├─ tracking.controller.ts  # referral clicks
│     ├─ core.ts                 # calcCommission()
│     └─ shopify.service.ts      # Admin API client
│
└─ apps/web/                    # Next.js 16 + React 19
   ├─ app/
   │  ├─ layout.tsx             # root + PublicHeader
   │  ├─ page.tsx               # landing
   │  ├─ admin/                 # CRM pages
   │  ├─ dashboard/             # affiliate portal
   │  ├─ signup/  · store/      # public pages
   │  └─ globals.css
   ├─ components/               # design system
   │  ├─ admin-shell · portal-shell
   │  ├─ ui · data-table · modal · charts
   │  └─ toast · settings-tabs · public-header
   └─ lib/api.ts                # fetch helper
Run locally
  1. Postgres up; set DATABASE_URL in apps/api/.env
  2. API: cd apps/apinpx prisma db pushnpm run start:dev (:4000)
  3. Web: cd apps/webnpm run dev (:3000)
  4. Shopify: add SHOPIFY_SHOP + SHOPIFY_ADMIN_TOKEN to the API .env

Scopes: read_orders, write_discounts, read_products. All admin data scoped via one getMerchant() helper (one brand per instance today).