Designing a Multi-Touch Affiliate & Referral Attribution System

Designing a Multi-Touch Affiliate & Referral Attribution System

Designing a Multi-Touch Affiliate & Referral Attribution System

How marketplaces like Amazon, Flipkart, Uber, and Airbnb track every click a referrer generates, follow a customer across days or weeks of browsing, and fairly decide who gets paid when a sale finally happens — explained plainly enough for a first-week engineer, and deep enough to walk into a senior systems interview with confidence.

01

Introduction & History

Imagine you run a small blog reviewing kitchen gadgets. You write a glowing review of a blender, put a special link at the bottom, and a reader clicks it, browses the marketplace for a week, gets distracted, comes back through a Google ad, then finally buys the blender ten days later. Should you get credit for that sale? Should the Google ad get credit? Should both? This exact question — “who caused this sale to happen” — is the entire reason affiliate and referral attribution systems exist.

Affiliate marketing is not new. It began in the mid-1990s when Amazon launched its “Associates Program” in 1996, letting website owners place links to Amazon products and earn a small commission on resulting sales. In those early days, attribution was almost embarrassingly simple: a cookie was dropped in the browser when someone clicked a link, and if that same browser bought something before the cookie expired, the click got 100% of the credit. This is called last-touch attribution, and for a single-referrer, single-device world, it worked fine.

But the internet changed. Customers now research a purchase across multiple devices — a phone during a commute, a laptop at work, a tablet at night. They encounter a brand through a YouTube influencer, then a coupon site, then a retargeting ad, then finally search the brand name directly and buy. A modern marketplace might have thousands of active referrers: bloggers, YouTubers, cashback apps, comparison sites, employee referral programs, and existing customers referring friends. The question “who gets the commission” became a genuinely hard distributed-systems and data-modeling problem, not just a cookie-dropping exercise.

This is why every serious marketplace today — Amazon, Flipkart, Myntra, Uber, Airbnb, Swiggy — runs a dedicated attribution platform: a backend system whose entire job is to capture every touchpoint a potential buyer has with a referral link, stitch those touchpoints together into one customer journey, and then apply a fair mathematical rule to decide how commission is split when a purchase eventually happens.

Real-life analogy

Think of a wedding that ten different people helped make happen: the friend who first introduced the couple, the family member who suggested a date, the caterer’s mutual friend who broke the ice, and the wedding planner who closed all the final details. If you had to divide a “thank you” gift fairly among all ten, you would not hand the entire gift to whoever happened to be standing next to the couple when they said “let’s do this.” You would think about who contributed early, who contributed at the critical moment, and how much each touchpoint mattered. Attribution systems do exactly this, mathematically, for every online sale.

We are going to design this system from scratch: how a click is captured, how a customer is recognized across multiple visits and devices, how the system decides which referrers deserve credit for a sale, how commissions are calculated and paid out, and how the whole thing is kept fast, fraud-resistant, and reliable at the scale of millions of clicks per day.

It is also worth understanding how privacy regulation has reshaped this space over the last decade, because it directly explains several architectural choices later in this tutorial. Regulations like the European Union’s GDPR and India’s Digital Personal Data Protection Act, 2023 impose real constraints on how long personal identifiers can be retained and how consent must be captured before tracking a visitor. At the same time, browser vendors independently began restricting third-party cookies well ahead of most regulation, forcing the entire industry toward first-party tracking domains, shorter default retention windows, and a much heavier reliance on explicit login events rather than passive fingerprinting for identity resolution. A modern attribution system, in other words, is not just an engineering problem — it operates inside a genuinely shifting legal and technical landscape, and durable designs build in configurability around retention windows and consent handling from day one rather than treating them as an afterthought.

1996Amazon Associates —last-click cookies 2000sNetworks & bloggeraffiliate boom 2010sCross-device;multi-touch models 2018+GDPR & ITP;first-party pivot TodayIdentity graphs,event-driven backbones
Fig 1.1 — From a single cookie to identity graphs: attribution has evolved to match how customers actually shop.
02

Problem & Motivation

Let us define the problem precisely before we design anything, because “attribution” means different things to different people and vague requirements produce bad architectures.

2.1 What We Are Actually Building

A marketplace wants to run a program where external parties — bloggers, influencers, comparison-shopping sites, cashback apps, or even ordinary customers referring friends — can generate special trackable links. When someone clicks such a link and eventually buys something, the referrer should earn a commission. The system must:

  • Generate and manage unique referral links and codes for thousands of referrers.
  • Track every click on those links in real time, at high volume.
  • Recognize the same visitor across multiple sessions, devices, and days (identity resolution).
  • Record every subsequent “touchpoint” that visitor has with the marketplace, not just the first click.
  • When a purchase happens, determine which touchpoint(s) deserve credit, using a configurable attribution model.
  • Calculate the correct commission amount per referrer per order line item.
  • Detect and block fraud — fake clicks, cookie stuffing, self-referrals, bot traffic.
  • Give referrers a dashboard showing clicks, conversions, and earnings.
  • Pay out earned commissions on a schedule, handling refunds and clawbacks correctly.

2.2 Why This Is Genuinely Hard

ChallengeWhy it is hard
Multi-touch journeysA single order might have five or more touchpoints spread across weeks. The system must remember all of them, not just the last one, without unbounded storage growth.
Cross-device identityThe same human might click a link on their phone but buy on a laptop. Cookies alone cannot bridge that gap.
ScaleA large marketplace may see tens of millions of clicks a day and needs sub-100-millisecond redirect latency on every single one — a slow redirect is a lost sale.
FraudMoney is directly at stake, so referrers have a financial incentive to game the system through fake clicks, cookie stuffing, or self-purchases.
Correctness of moneyCommission calculations must be auditable and reversible — refunds must claw back commissions, and every payout must be traceable to specific orders.
Attribution ambiguityThere is no single “correct” answer to who caused a sale. The business must choose a model (first-touch, last-touch, linear, time-decay, position-based) and the system must support recomputing history if that model changes.

2.3 Non-Functional Requirements

Beyond the functional list above, a handful of non-functional requirements shape almost every architectural decision made later in this tutorial, so it is worth stating them explicitly up front.

RequirementTargetWhy it matters
Redirect latencySub-100ms at p99Slow redirects measurably increase checkout abandonment; this is the single most customer-visible number in the whole system.
Click durabilityNo silent data lossA lost click can never be recovered — there is no secondary source of truth for “this person clicked this link at this moment.”
Commission correctnessCent-accurate, fully auditableDirectly affects real money owed to real people and must survive financial audits and legal disputes.
Availability of click pathVery high, multi-region resilientThe click-redirect endpoint effectively is the referral program from the customer’s point of view; if it is down, referred sales simply do not happen.
Attribution recomputabilityFully deterministic and replayableNeeded for audits, model changes, and dispute resolution long after the original events occurred.
Why naive solutions fail

A common first instinct is: “just store a cookie with the referrer ID, and whoever’s cookie is present at checkout gets the commission.” This is last-click attribution baked directly into the checkout code. It seems simple, but it silently punishes the blogger who did the actual persuasion work, rewards low-value “coupon code” sites that intercept buyers seconds before checkout, and gives the business zero flexibility to ever change how they think about attribution — because the logic is scattered across the checkout flow instead of centralized in a dedicated engine.

03

Core Concepts

3.1 Referral Link and Referral Code

A referral code is a short unique identifier tied to a referrer, for example REF-AJAY123. A referral link embeds that code into a URL, such as https://marketplace.com/product/blender-9000?ref=AJAY123. When a marketplace supports deep linking into a mobile app, the same code also needs to survive an app install — this uses techniques like deferred deep linking, where the code is temporarily stored server-side and matched to the app’s first open using device fingerprint signals.

Beginner example

Think of a referral code like a coat-check ticket. The ticket itself does not hold your coat — it just points to where the coat is stored. Similarly, ref=AJAY123 does not hold any commission data itself; it just points the system to Ajay’s referrer record, which lives safely in a database.

3.2 Click, Touchpoint, and Session

A click is the raw event: someone clicked a referral link at a specific timestamp, from a specific IP, device, and browser. A touchpoint is a slightly richer concept: any recorded interaction that could plausibly influence a future purchase — this includes clicks on referral links, but can also include opening a push notification, clicking an email campaign link, or engaging with a retargeting ad. A session groups touchpoints that happen close together in time from the same device into one browsing visit, typically closed after 30 minutes of inactivity.

3.3 Identity Resolution

Because the same person may use multiple devices, the system needs a way to link a phone-session and a laptop-session to one underlying “customer journey.” This is done using a combination of: a persistent first-party cookie, a probabilistic device fingerprint (screen size, browser version, timezone — used cautiously due to privacy regulation), and, most reliably, an actual login. Once someone logs in on any device, all prior anonymous sessions with a matching cookie or fingerprint can be merged into a single identity graph.

3.4 Attribution Window

An attribution window (also called a “cookie lifetime” or “lookback window”) is the maximum time between a touchpoint and a purchase for that touchpoint to still count. A typical e-commerce marketplace uses a 7 to 30 day window. If Ajay’s link was clicked 45 days before a purchase and the window is 30 days, that click is too old to receive credit.

3.5 Attribution Models

This is the mathematical heart of the system — the rule for splitting credit among multiple touchpoints in one customer journey.

ModelRuleBest for
First-touch100% of credit to the very first touchpoint in the journey.Rewarding top-of-funnel discovery, like a blogger who introduces a brand.
Last-touch100% of credit to the touchpoint immediately before purchase.Simple programs, or rewarding closers like cashback/coupon sites.
LinearCredit split evenly across every touchpoint in the journey.Programs that value every interaction equally.
Time-decayTouchpoints closer to the purchase get exponentially more credit than earlier ones.Balancing “closers” with some credit to earlier influence.
Position-based (U-shaped)A fixed percentage, commonly 40%, to the first touchpoint, 40% to the last, and the remaining 20% split among the middle ones.Valuing both discovery and the final push, a very common industry default.
📌
What an interviewer may ask
  • “How would you design the data model so that switching attribution models does not require re-architecting the system?” — Answer: store the raw touchpoint history immutably, and treat the attribution model as a pure function applied at commission-calculation time, not baked into the write path.
  • “What happens if the business changes the attribution model after commissions have already been paid?” — Discuss recomputation, versioning of attribution rules, and why you never mutate historical commission records, only issue adjustments.

3.6 Conversion and Commission

A conversion is the target action — usually a completed and paid order — that the referral program rewards. A commission is the money owed to a referrer, computed as a percentage of order value (or a flat amount per conversion), split across eligible touchpoints according to the attribution model, and adjusted for taxes, refunds, and program-specific caps.

3.7 Cookie Mechanics in Detail

It helps to understand exactly what a “referral cookie” actually contains, since a lot of attribution correctness comes down to getting this small piece of data right. A well-designed referral cookie is not just a raw referral code — it typically stores a JSON-like payload containing the anonymous visitor ID, the referral code that first (or most recently) touched this browser, a timestamp, and an expiry that matches the attribution window exactly. First-party cookies (set by the marketplace’s own domain) are far more reliable than third-party cookies, because most modern browsers — Safari’s Intelligent Tracking Prevention, Firefox’s Enhanced Tracking Protection, and Chrome’s ongoing phase-out of third-party cookies — aggressively block or shorten the lifespan of third-party tracking cookies. This is precisely why the Click Tracking Service in this design always redirects through the marketplace’s own domain rather than an external tracking domain: it keeps the cookie first-party and therefore durable enough to survive the full attribution window.

Software example

A first-party cookie set on mkt.com when a user clicks a link on mkt.com/r/... can reliably persist for the full 30-day attribution window. A third-party cookie set by a separate tracking domain like tracker.example.com embedded as a pixel on mkt.com might be wiped by the browser within 24 hours or blocked outright — silently breaking attribution for a huge share of Safari and Firefox users. This single architectural decision — first-party versus third-party cookie domains — has a bigger real-world impact on attribution accuracy than almost any algorithm choice.

3.8 UTM Parameters and Campaign Metadata

Beyond the referral code itself, marketplaces typically also capture UTM parameters (utm_source, utm_medium, utm_campaign) on every click. These do not usually affect who gets paid, but they let the Reporting and Analytics Service break down performance by campaign — for example, showing a referrer that their Instagram-story links convert twice as well as their blog-post links, even though both use the same underlying referral code and both are eligible for the same commission.

3.9 Attribution Window Edge Cases

The attribution window sounds simple — “30 days from click to purchase” — but real systems have to handle several edge cases carefully:

  • Renewing subscriptions: if the marketplace sells subscriptions, does a referral get credit only for the first payment, or for every renewal? Most programs credit only the first conversion, to avoid indefinitely paying a referrer for a customer relationship the marketplace itself now owns.
  • Window extension on repeat clicks: if a customer clicks the same referral link twice, five days apart, does the window restart from the second click? Most systems treat this as extending, not restarting, the window, using the earliest qualifying click for first-touch credit while still counting the later click as a separate touchpoint for models like linear or position-based.
  • Cross-program conflicts: what if a customer is inside the attribution window for two different, unrelated referral programs (say, a general affiliate link and a friend-referral code) at the same time? Program rules typically define priority order or allow both programs to earn independently, since they are drawing from different budgets.

3.10 Deep Linking Into Mobile Apps

Attribution gets meaningfully harder when the destination is a mobile app rather than a web page, because a click on a referral link often happens in a mobile browser, but the actual purchase happens after the user installs and opens a completely separate native app — and a native app has no access to the browser’s cookies. The standard solution is deferred deep linking: when the Click Tracking Service detects that the destination requires the app and the app is not yet installed, it stores the click’s visitor ID and referral code server-side, redirects to the app store, and then, on first app open, the app calls a matching endpoint with device signals (install time, device model, IP, timezone). The server performs a best-effort probabilistic match against recent unmatched clicks and, if confident enough, stitches the pre-install click into the user’s post-install journey.

04

Architecture & Components

Here is the full system, drawn as a set of layered components. Every box below explicitly shows where API Gateway and Load Balancer sit relative to it, since these two components sit in front of nearly every service call in this system.

Client Layer Customer Browser / Mobile App Referrer Dashboard Web App Edge Layer CDN — static + redirect cache Load Balancer — L7, autoscaled API Gateway — auth, rate limit Core Services (behind LB + API Gateway) Click Trackingstateless, hot path Identity Resolutionjourney merge Attribution Enginemodel application Commission Calcledger writer Fraud Detectionscoring pipeline Payout Servicebatched transfers Reporting / Analyticsdashboards Referral Link Mgmtcode → destination Kafka — click, touchpoint, order & fraud-score topicsevent backbone (partitioned by visitor ID) Data Layer PostgreSQLreferrers, commissions,payouts (ACID) Cassandraclick / touchpointevent store Redislink cache, sessions,rate limits Data Warehouseanalytics, reporting,dispute lookup
Fig 4.1 — High-level architecture. Every client request passes through the CDN, Load Balancer, and API Gateway before reaching any service; every core service sits behind that same Load Balancer and API Gateway pairing.

4.1 Component Responsibilities

Edge

CDN

Caches static dashboard assets and, importantly, can serve extremely fast redirects for referral links at the edge, closer to the user, before the request even reaches the origin Load Balancer. For very high-traffic referral links — say, a promoted link during a flash sale — the CDN can hold a short-lived edge redirect rule so millions of clicks never touch origin infrastructure at all. The trade-off is that edge-cached redirects cannot carry the full click-logging logic, so systems typically use a “log-then-cache” pattern: the first click for a given code hits origin and populates the edge cache, and every following click within the cache TTL gets both the fast edge redirect and an asynchronous beacon-based click log.

Edge

Load Balancer

Distributes incoming traffic across many instances of the API Gateway and, indirectly, every downstream service. Performs health checks and removes unhealthy nodes automatically, typically using a mix of TCP-level liveness checks and HTTP-level readiness checks that verify a service can actually reach its own dependencies before being sent live traffic. Every service in this architecture sits behind it, and it is usually configured with connection draining so in-flight requests complete cleanly during a deploy or scale-down event rather than being abruptly terminated.

Edge

API Gateway

The single entry point for all client and referrer-dashboard traffic. Handles authentication (referrer API keys, dashboard JWTs), rate limiting per referrer, request routing to the correct microservice, and basic request validation before anything touches business logic. It also centralizes cross-cutting concerns like request logging, CORS policy, and response compression, so individual microservices do not each need to reimplement them. Because it sits in front of every service, the Gateway is also a natural place to enforce a global circuit breaker that protects the whole platform if one downstream service starts failing broadly.

Service

Referral Link Management

Lets referrers create, view, and deactivate referral links and codes. Owns the mapping from short code to destination product or landing page, and enforces business rules like preventing a referrer from generating links to products outside their approved category, or capping the number of active links per account to reduce abuse surface. Sits behind the Load Balancer and API Gateway like every other service here, and publishes a cache-invalidation event to Redis whenever a link is edited or deactivated so the Click Tracking Service never serves a stale mapping.

Service

Click Tracking Service

The hot path. Receives every click on a referral link, resolves the short code, logs the raw click event to Kafka, sets or reads a first-party cookie, and issues an HTTP redirect to the real destination — all within a tight latency budget, behind the Load Balancer and API Gateway. It is intentionally kept as thin and stateless as possible: no business logic beyond cookie handling and event publishing lives here, because every extra millisecond of processing on this path is multiplied across tens of millions of daily requests.

Service

Identity Resolution

Consumes click and session events from Kafka and maintains an identity graph, merging anonymous cookie-based identities with logged-in user identities so a journey can be reconstructed across devices. It uses a union-find style data structure internally: every new anonymous ID starts as its own node, and a login event that matches a known cookie merges that node into the logged-in user’s cluster, so a lookup for “give me this customer’s full journey” can efficiently walk the merged graph instead of scanning every session ever recorded.

Service

Fraud Detection

Scores clicks and conversions in near-real-time for suspicious patterns — click farms, cookie stuffing, self-referral — and can quarantine touchpoints before they ever influence a commission payout. It combines rule-based checks (velocity limits, IP reputation lists, known data-center IP ranges that real consumers rarely browse from) with a lightweight statistical model trained on historical confirmed-fraud cases, and every score it produces is logged so a human reviewer can later audit why a specific click or conversion was flagged.

Service

Attribution Engine

The mathematical core. On every conversion event, retrieves the full touchpoint history for that customer journey within the attribution window and applies the configured attribution model to split credit. It is designed as a pure function over its inputs — given the same journey and the same model, it always produces the same split — which is what makes it safe to re-run for reconciliation, audits, or backfills after a bug fix.

Service

Commission Calculation

Turns attributed credit into actual currency amounts, applying referrer-specific commission rates, program rules, tax handling, and caps, then writes an auditable commission ledger entry. It also enforces program-level guardrails, such as a maximum commission per order or a minimum order value threshold below which no commission is paid, both of which protect the marketplace’s margin on very low-value transactions.

Service

Payout Service

Batches approved commissions on a schedule (e.g. monthly), integrates with payment rails, and handles retries, holds, and clawbacks for refunded orders. It maintains its own state machine per payout batch — pending, processing, completed, failed, retried — so operators always have clear visibility into exactly where a specific referrer’s money currently sits in the pipeline.

Service

Reporting & Analytics

Powers the referrer dashboard and internal marketplace analytics — clicks, conversion rate, top referrers — reading from the data warehouse rather than the live transactional stores to avoid impacting production traffic. It typically pre-aggregates common queries (daily clicks per referrer, monthly commission totals) into materialized summary tables so dashboard loads stay fast even as the underlying raw event volume grows into the billions of rows.

05

Internal Working

5.1 Anatomy of a Single Click

When a customer clicks https://mkt.com/r/blender9000?ref=AJAY123, here is exactly what happens, step by step, all behind the Load Balancer and API Gateway:

  1. DNS resolves to the CDN edge node closest to the user.
  2. The CDN checks if it has a cached redirect rule for this exact short link; if the destination rarely changes, it can redirect immediately without contacting the origin — shaving tens of milliseconds off latency.
  3. If not cached, the request passes through the Load Balancer to the API Gateway.
  4. The API Gateway performs lightweight validation (is this a well-formed referral code, is the referrer’s account active) and routes to the Click Tracking Service.
  5. The Click Tracking Service looks up the referral code in Redis (hot cache) or PostgreSQL (cold fallback) to find the destination URL and referrer ID.
  6. It checks for an existing first-party cookie identifying this browser; if none exists, it generates a new anonymous visitor ID and sets a cookie with an expiry matching the attribution window.
  7. It publishes a click event to Kafka containing: visitor ID, referrer ID, referral code, timestamp, product ID, IP (hashed for privacy), user agent, and any UTM parameters.
  8. It responds with an HTTP 302 redirect straight to the product page.

All of this — cache lookup, cookie logic, event publish, redirect — must happen inside a tight latency budget, typically under 100 milliseconds, because every extra millisecond of redirect delay measurably increases the chance the user abandons before the destination page even loads.

5.2 Sequence: Multi-Touch Journey to Conversion

User LB API GW Click Svc Kafka Identity Attribution Comm Day 1 — click on blogger’s referral link publish click 302 redirect Day 5 — same visitor clicks a different referral ad publish click (same visitorId) consume clicks merge sessions Day 8 — user purchases the blender order-confirmed consume order event apply model attributed credits order complete, commissions recorded
Fig 5.1 — A customer touches two different referral sources across eight days before purchasing. The Attribution Engine reconstructs the full journey and splits credit between both touchpoints.

5.3 Java: Click Event Ingestion

The Click Tracking Service publishes a well-structured event rather than raw HTTP parameters, so every downstream consumer works with a stable contract.

ClickRedirectController.java — the hot path
@RestController
@RequestMapping("/r")
public class ClickRedirectController {

    private final ReferralLinkCache linkCache;
    private final KafkaTemplate<String, ClickEvent> kafkaTemplate;
    private final CookieService cookieService;

    @GetMapping("/{shortCode}")
    public ResponseEntity<Void> handleClick(
            @PathVariable String shortCode,
            HttpServletRequest request,
            HttpServletResponse response) {

        ReferralLink link = linkCache.resolve(shortCode);
        if (link == null || !link.isActive()) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }

        String visitorId = cookieService.getOrCreateVisitorId(
                request, response, link.getAttributionWindowDays());

        ClickEvent event = ClickEvent.builder()
                .visitorId(visitorId)
                .referrerId(link.getReferrerId())
                .referralCode(shortCode)
                .productId(link.getProductId())
                .timestamp(Instant.now())
                .ipHash(IpHasher.hash(request.getRemoteAddr()))
                .userAgent(request.getHeader("User-Agent"))
                .build();

        // Fire-and-forget publish keeps redirect latency low.
        kafkaTemplate.send("click-events", visitorId, event);

        response.setHeader("Location", link.getDestinationUrl());
        return ResponseEntity.status(HttpStatus.FOUND).build();
    }
}

5.4 Java: Attribution Engine Core Logic

AttributionEngine.java — strategy-based credit split
public class AttributionEngine {

    public List<AttributedCredit> attribute(
            List<Touchpoint> journey,
            AttributionModel model,
            BigDecimal orderValue) {

        // Only touchpoints inside the attribution window are eligible.
        List<Touchpoint> eligible = journey.stream()
                .filter(Touchpoint::isWithinAttributionWindow)
                .sorted(Comparator.comparing(Touchpoint::getTimestamp))
                .collect(Collectors.toList());

        if (eligible.isEmpty()) {
            return Collections.emptyList();
        }

        switch (model) {
            case FIRST_TOUCH:
                return List.of(fullCredit(eligible.get(0)));

            case LAST_TOUCH:
                return List.of(fullCredit(eligible.get(eligible.size() - 1)));

            case LINEAR:
                BigDecimal equalShare = BigDecimal.ONE
                        .divide(BigDecimal.valueOf(eligible.size()), 6, RoundingMode.HALF_UP);
                return eligible.stream()
                        .map(tp -> new AttributedCredit(tp, equalShare))
                        .collect(Collectors.toList());

            case POSITION_BASED:
                return applyPositionBased(eligible);

            case TIME_DECAY:
                return applyTimeDecay(eligible, orderValue);

            default:
                throw new IllegalArgumentException("Unsupported model: " + model);
        }
    }

    private List<AttributedCredit> applyPositionBased(List<Touchpoint> eligible) {
        if (eligible.size() == 1) {
            return List.of(fullCredit(eligible.get(0)));
        }
        List<AttributedCredit> result = new ArrayList<>();
        BigDecimal firstShare = BigDecimal.valueOf(0.40);
        BigDecimal lastShare = BigDecimal.valueOf(0.40);
        int middleCount = eligible.size() - 2;
        BigDecimal middleShare = middleCount == 0
                ? BigDecimal.ZERO
                : BigDecimal.valueOf(0.20).divide(BigDecimal.valueOf(middleCount), 6, RoundingMode.HALF_UP);

        for (int i = 0; i < eligible.size(); i++) {
            Touchpoint tp = eligible.get(i);
            if (i == 0) {
                result.add(new AttributedCredit(tp, firstShare));
            } else if (i == eligible.size() - 1) {
                result.add(new AttributedCredit(tp, lastShare));
            } else {
                result.add(new AttributedCredit(tp, middleShare));
            }
        }
        return result;
    }

    private AttributedCredit fullCredit(Touchpoint tp) {
        return new AttributedCredit(tp, BigDecimal.ONE);
    }
}
📌
What an interviewer may ask

“Why use BigDecimal instead of double for commission math?” — Because floating-point arithmetic introduces rounding errors that compound over millions of transactions, and money must reconcile to the cent. Always discuss fixed-point or BigDecimal arithmetic for anything financial.

5.5 Key Algorithms and Data Structures

A few classic data structures and algorithms show up repeatedly once you start building this system for real, and interviewers often probe on exactly these choices.

5.5.1 Sliding Window Rate Limiting

To stop click-fraud bots from hammering a single referral link thousands of times a second, the Click Tracking Service applies a sliding-window rate limiter per IP-and-referral-code pair, backed by Redis sorted sets. Each click adds a timestamped entry; before accepting a click, the service trims entries older than the window and checks whether the remaining count exceeds the threshold.

SlidingWindowRateLimiter.java — Redis-backed sliding window
public class SlidingWindowRateLimiter {

    private final RedisTemplate<String, String> redis;
    private final int maxRequests;
    private final Duration window;

    public boolean allow(String key) {
        long now = System.currentTimeMillis();
        long windowStart = now - window.toMillis();
        String redisKey = "rl:" + key;

        redis.opsForZSet().removeRangeByScore(redisKey, 0, windowStart);
        Long count = redis.opsForZSet().zCard(redisKey);

        if (count != null && count >= maxRequests) {
            return false;
        }

        redis.opsForZSet().add(redisKey, UUID.randomUUID().toString(), now);
        redis.expire(redisKey, window);
        return true;
    }
}

5.5.2 Union-Find for Identity Graph Merging

The Identity Resolution Service’s core job — merging anonymous sessions into one journey once a login event bridges them — maps naturally onto the classic union-find (disjoint-set) data structure, giving near-constant-time merges and lookups even as the identity graph grows to hundreds of millions of nodes.

IdentityUnionFind.java — disjoint-set with path compression
public class IdentityUnionFind {

    private final Map<String, String> parent = new ConcurrentHashMap<>();

    public String find(String id) {
        String root = id;
        while (!parent.getOrDefault(root, root).equals(root)) {
            root = parent.get(root);
        }
        // Path compression for future lookups.
        parent.put(id, root);
        return root;
    }

    public void union(String idA, String idB) {
        String rootA = find(idA);
        String rootB = find(idB);
        if (!rootA.equals(rootB)) {
            parent.put(rootB, rootA);
        }
    }
}

In production this in-memory sketch is backed by a persistent store (Cassandra or a graph database) partitioned so that lookups for a given visitor’s canonical identity stay fast even at scale, with an in-memory LRU cache in front for the hottest recently-active visitors.

5.5.3 Bloom Filter for Duplicate Click Suppression

A single accidental double-tap or a retried HTTP request can generate two identical click events. Rather than paying for an exact-match database lookup on every single click just to catch this rare case, the Click Tracking Service keeps a probabilistic Bloom filter of recently seen click fingerprints (visitor ID plus referral code plus a coarse timestamp bucket) in memory. A Bloom filter can never produce a false negative, so if it says “not seen before,” the click is definitely new; a small, tunable false-positive rate is an acceptable trade-off for avoiding a database round-trip on every request.

5.5.4 Consistent Hashing for Kafka Partitioning

Click and touchpoint events are partitioned in Kafka by visitor ID using consistent hashing, which guarantees that every event for the same visitor lands on the same partition and is therefore processed in strict order by the same consumer instance — essential for the Identity Resolution Service and Attribution Engine, both of which need to see a visitor’s events in chronological order to build a correct journey.

5.6 Java: Commission Calculation Service

Once the Attribution Engine has split credit across touchpoints, the Commission Service converts those shares into real currency, applies referrer-specific rates and program guardrails, and writes an idempotent ledger entry.

CommissionCalculationService.java — idempotent ledger writer
@Service
public class CommissionCalculationService {

    private final ReferrerRateRepository rateRepository;
    private final CommissionLedgerRepository ledgerRepository;

    @Transactional
    public void calculateAndRecord(String orderId, BigDecimal orderValue,
                                    List<AttributedCredit> credits) {

        // Idempotency guard: never double-write for the same order.
        if (ledgerRepository.existsByOrderId(orderId)) {
            return;
        }

        for (AttributedCredit credit : credits) {
            ReferrerRate rate = rateRepository.findActiveRate(
                    credit.getTouchpoint().getReferrerId());

            BigDecimal eligibleValue = orderValue.multiply(credit.getShare());
            BigDecimal commission = eligibleValue
                    .multiply(rate.getCommissionPercent())
                    .setScale(2, RoundingMode.HALF_UP);

            commission = applyProgramCap(commission, rate);

            CommissionLedgerEntry entry = CommissionLedgerEntry.builder()
                    .orderId(orderId)
                    .referrerId(credit.getTouchpoint().getReferrerId())
                    .attributionShare(credit.getShare())
                    .commissionAmount(commission)
                    .status(CommissionStatus.PENDING_APPROVAL)
                    .createdAt(Instant.now())
                    .build();

            ledgerRepository.save(entry);
        }
    }

    private BigDecimal applyProgramCap(BigDecimal commission, ReferrerRate rate) {
        if (rate.getMaxCommissionPerOrder() != null
                && commission.compareTo(rate.getMaxCommissionPerOrder()) > 0) {
            return rate.getMaxCommissionPerOrder();
        }
        return commission;
    }
}

5.7 Java: Fraud Scoring on the Click Path

Fraud scoring must stay asynchronous relative to the redirect, so it runs as a Kafka consumer rather than an inline call, producing a score that later gates whether a touchpoint is eligible for attribution.

FraudScoringConsumer.java — async scoring pipeline
@KafkaListener(topics = "click-events", groupId = "fraud-detection")
public class FraudScoringConsumer {

    private final IpReputationClient ipReputation;
    private final ClickVelocityStore velocityStore;
    private final KafkaTemplate<String, FraudScoreEvent> scoreTopic;

    public void onClick(ClickEvent event) {
        int score = 0;

        if (ipReputation.isDataCenterIp(event.getIpHash())) {
            score += 40;
        }

        int recentClicksFromIp = velocityStore.countInWindow(
                event.getIpHash(), Duration.ofMinutes(1));
        if (recentClicksFromIp > 20) {
            score += 35;
        }

        if (velocityStore.isKnownSelfReferral(event.getVisitorId(), event.getReferrerId())) {
            score += 50;
        }

        FraudScoreEvent scored = new FraudScoreEvent(
                event.getVisitorId(), event.getReferralCode(), score, Instant.now());

        scoreTopic.send("fraud-scores", event.getVisitorId(), scored);
        velocityStore.recordClick(event.getIpHash());
    }
}

A configurable threshold (commonly 70 or higher on a 0-100 scale) determines whether a touchpoint is automatically excluded from attribution eligibility, routed to manual review, or allowed through — giving the business a tunable dial between aggressive fraud prevention and avoiding false positives that wrongly deny genuine referrers their earned commission.

06

Data Flow & Lifecycle

Let us trace one referral-driven order from click to payout, showing exactly which store owns each piece of data.

Click Capturedby Click Tracking Svc Cassandra Event Storeappend-only write Kafka click-eventsevent backbone Identity Resolutionjourney merge Redis journey cachefast lookup at conversion Customer Purchasespayment confirmed Kafka order-confirmedevent published Attribution Enginefetch + apply model Commission Calcshares → currency PostgreSQL Ledgersource of truth for money Payout Servicescheduled batch Referrerfunds paid Data Warehousereporting + analytics
Fig 6.1 — End-to-end lifecycle from a single click to a paid-out commission, plus the parallel reporting path.

6.1 Lifecycle Stages Explained

  1. Capture: Click Tracking Service writes the raw event to Kafka within milliseconds of the click.
  2. Persistence: A Kafka consumer writes the event into Cassandra, chosen for its write-heavy, append-only workload characteristics and natural time-series partitioning by visitor ID.
  3. Identity Merge: The Identity Resolution Service periodically or reactively merges sessions belonging to the same underlying person, updating a lightweight journey summary cached in Redis for fast retrieval at conversion time.
  4. Conversion Trigger: When an order is marked paid (not just placed — refund risk means marketplaces usually wait for payment confirmation), the Order Service emits an order-confirmed event.
  5. Attribution: The Attribution Engine consumes that event, pulls the visitor’s full touchpoint history from Cassandra (using the Redis-cached journey as a fast path), filters to the attribution window, and applies the configured model.
  6. Commission Calculation: Attributed credit shares are converted into actual currency, referrer rates applied, and a ledger entry written to PostgreSQL — the system of record for money.
  7. Payout: On a schedule, the Payout Service aggregates approved, non-refund-risk commissions per referrer and initiates a transfer.
  8. Refund Handling: If an order is later refunded, a compensating order-refunded event flows through the same pipeline, generating a negative commission ledger entry (a clawback) rather than deleting the original record — preserving a full audit trail.
Why immutability matters here

Every event in this system — clicks, touchpoints, commission ledger entries — is append-only. Nothing is ever updated in place. A refund does not erase a commission; it adds an offsetting entry. This is not a stylistic choice — it is a legal and accounting requirement. Auditors and referrers must be able to reconstruct exactly why they were paid a specific amount on a specific date.

6.2 Handling Real-World Edge Cases

A handful of scenarios come up constantly in production and are worth designing for explicitly rather than discovering as incidents later.

  • Multi-item orders with mixed eligibility: A single order might contain one product that was reached via a referral link and another that was not. The Attribution Engine operates at the order-line-item level, not the whole-order level, so only the eligible line items generate commission, and the ledger entry references the specific line item, not just the order as a whole.
  • Order cancelled before payment confirms: Because attribution only triggers on an order-confirmed (paid) event rather than an order-placed event, an order that is abandoned or cancelled at the payment step never enters the attribution pipeline at all, avoiding the need to generate and then reverse a commission that was never really earned.
  • Partial refunds: If only part of an order is refunded, the compensating clawback event carries the exact refunded line-item amount, and the Commission Service recalculates a proportional negative adjustment rather than clawing back the full original commission.
  • Referrer account suspended after the click but before conversion: The touchpoint itself remains a valid historical record, but the Commission Service checks referrer account status at calculation time, and a suspended referrer’s otherwise-eligible commission is held in a pending-review state rather than either being silently paid or silently discarded.
07

Advantages, Disadvantages & Trade-offs

Advantages

Dedicated attribution platform

  • Attribution logic is centralized and can evolve without touching checkout code.
  • Supports sophisticated multi-touch models, which are fairer to referrers and encourage higher-quality referral traffic, not just last-second coupon interception.
  • Full audit trail supports finance, tax, and dispute resolution.
  • Fraud detection can be layered in without disrupting the tracking pipeline.
Disadvantages

Costs of this approach

  • Significant infrastructure complexity compared to a simple last-click cookie.
  • Multi-touch storage and identity resolution add real operational cost at scale.
  • Cross-device identity resolution is inherently probabilistic and never 100% accurate.
  • Recomputing historical attribution after a model change is expensive and must be handled carefully to avoid double-paying commissions.

7.1 Key Trade-off: Attribution Window Length

A longer attribution window (say, 30 days) is more generous to referrers and captures more of the true buying journey, but increases storage retention requirements and makes fraud detection harder because more time passes between click and conversion. A shorter window (say, 3 days) is cheaper and simpler but may unfairly deny credit to a blogger whose review genuinely convinced someone who took two weeks to actually buy.

7.2 Key Trade-off: Real-Time vs Batch Attribution

Real-time attribution (running the Attribution Engine synchronously the moment an order is confirmed) gives referrers instant dashboard visibility but requires the journey data to always be fast to query. Batch attribution (running a nightly job over all of that day’s orders) is operationally simpler and cheaper, but referrers see a delay before knowing they earned a commission. Most large-scale systems use a hybrid: a fast, approximate real-time attribution for dashboard display, and an authoritative nightly batch recomputation for the actual commission ledger.

7.3 Key Trade-off: Probabilistic vs Deterministic Identity Resolution

Relying only on deterministic identity signals — first-party cookies and explicit logins — is precise and privacy-friendly, but understates true cross-device journeys, since a large share of customers never log in until the moment they actually check out. Adding probabilistic signals — device fingerprinting, IP and timezone matching — recovers more of the true journey and generally increases attributed credit for early-funnel referrers, but introduces false-positive risk (incorrectly merging two different people’s sessions) and raises privacy and regulatory considerations that must be weighed against the attribution accuracy gained.

08

Performance & Scalability

The Click Tracking Service is the single hottest path in this entire architecture, because every click must redirect fast or the marketplace loses the sale regardless of who gets credit for it.

8.1 Scaling the Click Path

  • Horizontal scaling: The Click Tracking Service is stateless — all state lives in Redis and Kafka — so it scales horizontally behind the Load Balancer with simple autoscaling rules based on request rate.
  • Caching referral link lookups: Referral code to destination mappings rarely change, so they are cached in Redis with a long TTL and refreshed via a cache-invalidation event when a referrer updates a link.
  • Asynchronous event publishing: The redirect response never waits for Kafka to confirm the write to disk; it uses a fire-and-forget or fast-acknowledge publish so redirect latency is not coupled to storage durability.
  • Edge redirects: Where possible, redirect rules are pushed to CDN edge nodes so the busiest links never even hit the origin infrastructure.

8.2 Scaling the Attribution Engine

Unlike the click path, the Attribution Engine is not latency-critical for the customer — it runs after the order is already placed. This gives room to use Kafka consumer groups to parallelize processing across partitions (partitioned by visitor ID, which also conveniently keeps a single journey’s events in order), and to batch-fetch touchpoint history from Cassandra using efficient partition-key range queries instead of scattered point lookups.

8.3 Capacity Planning Example

MetricExample scale
Peak clicks per second5,000 – 20,000 during flash sales
Redirect latency target (p99)Under 100 ms
Orders processed per day500,000 – 2,000,000
Average touchpoints per converting journey2 – 6
Attribution window7 – 30 days, configurable per program

It is worth walking through how these numbers translate into actual infrastructure sizing, since this is exactly the kind of estimation an interviewer will often ask for. At a peak of 20,000 clicks per second, and assuming each Click Tracking Service instance can comfortably sustain around 2,000 requests per second while staying under the 100ms p99 latency budget, the fleet needs roughly ten to fifteen active instances at peak, with autoscaling policies keeping a smaller baseline fleet running during off-peak hours and scaling out ahead of known high-traffic events like sales. On the storage side, if each click event is roughly 500 bytes once serialized, 20,000 clicks per second works out to about 10 megabytes per second of raw ingest at peak, or a little over 800 gigabytes per day if that peak were sustained continuously — in practice traffic is far spikier than that, so real daily volume is usually a fraction of the theoretical peak-sustained figure, but sizing Kafka partition counts and broker throughput against the peak number, not the average, is what keeps the system from falling behind during exactly the moments — flash sales — when referral traffic and revenue both matter most.

📌
What an interviewer may ask

“Where is the bottleneck likely to appear first as this system scales to 10x traffic?” — A strong answer identifies the Click Tracking Service’s dependency on Redis for referral link lookups, and discusses read replicas, local in-memory caching with short TTLs, and Kafka partition count as the levers to pull before the database layer becomes the constraint.

8.4 CAP Theorem and Consistency Choices

The CAP theorem — that a distributed system can only fully guarantee two of Consistency, Availability, and Partition tolerance at once — plays out concretely across this architecture, and different components deliberately land in different places on that spectrum.

ComponentChoiceReasoning
Click Tracking + Redis link cacheFavors Availability over strict Consistency (AP)Serving a slightly stale referral link mapping for a few seconds after an edit is far less costly than failing the redirect entirely.
Commission Ledger in PostgreSQLFavors Consistency over Availability (CP)Money must never be double-counted or lost; a brief unavailability during a partition is preferable to writing an incorrect commission.
Cassandra click/touchpoint storeTunable, typically AP with eventual consistencyIndividual click writes can tolerate eventual consistency across replicas since the Attribution Engine reads a full history window, not a single latest-value read.

This mixed approach — sometimes called “polyglot consistency” — is a deliberate design choice: it would be wasteful to force the entire system into the strictest consistency model just because one component (money) genuinely needs it.

8.5 Concurrency Considerations

Two concurrency hazards deserve special attention in this system. First, two nearly-simultaneous orders from the same visitor (rare, but possible with duplicate browser tabs) could race to read the same touchpoint history and both trigger commission calculation; this is prevented by making the Commission Service’s write to PostgreSQL use an idempotency key derived from the order ID, so a duplicate calculation simply upserts the same row rather than creating two ledger entries. Second, the Kafka consumer groups for the Attribution Engine must guarantee in-order processing per visitor, which is why partitioning by visitor ID (not round-robin) is a hard architectural requirement, not just an optimization — processing a purchase event before its preceding click events would corrupt the journey.

09

High Availability & Reliability

A dropped click means lost commission-attribution data forever — there is no way to retroactively recover “someone clicked this link at this moment” if the event is lost. Reliability here is about never losing that signal, even if downstream processing is temporarily delayed.

  • Multi-AZ deployment: The Click Tracking Service, Load Balancer, and Kafka brokers are deployed across multiple availability zones so a single data center failure does not take down link tracking.
  • Kafka replication: Click event topics are replicated across at least three brokers with acks=all for the small subset of critical writes (like order-confirmed events), while high-volume click events can use a slightly relaxed acknowledgment setting favoring throughput, since occasional click loss is tolerable but order-event loss is not.
  • Graceful degradation: If Redis is briefly unavailable, the Click Tracking Service falls back to a direct (slower) PostgreSQL read for referral link resolution rather than failing the redirect entirely.
  • Idempotent consumers: Every downstream consumer (Identity Resolution, Attribution Engine, Commission Service) is built to safely process a duplicate event without double-crediting a commission, using deterministic event IDs and upsert semantics.
  • Circuit breakers: Calls from the Attribution Engine to the Commission Service use a circuit breaker so a slow or failing Commission Service does not cascade backpressure into the Kafka consumer group and stall the entire pipeline.
Production example

Large marketplaces design their click-redirect path to survive even a full regional outage of their attribution backend. If Kafka or the event pipeline is degraded, the redirect itself still succeeds instantly — the click is logged to a local durable buffer and replayed once the pipeline recovers. Losing attribution credit temporarily is acceptable; losing the sale itself is not.

9.1 Disaster Recovery and Backup Strategy

Because the PostgreSQL commission ledger is the financial system of record, it follows a strict backup regime: continuous write-ahead-log shipping to durable object storage, point-in-time recovery capability going back at least 35 days, and a warm standby replica in a second region that can be promoted within minutes if the primary region fails entirely. Cassandra’s own multi-node replication provides much of its own resilience, but nightly snapshots are still taken and shipped off-cluster as a safeguard against a correlated failure or an operational mistake like an accidental mass delete.

Recovery objectives are set explicitly per data store, since they do not all carry the same business risk: the commission ledger targets a Recovery Point Objective (RPO) of near zero — essentially no acceptable data loss — and a Recovery Time Objective (RTO) of minutes, while the click-event store can tolerate a slightly looser RPO of a few minutes, since even losing a short window of raw click data is recoverable in the aggregate business sense, whereas losing committed financial records is not.

9.2 Consensus and Replication in Kafka

Kafka’s own reliability rests on a consensus mechanism among broker replicas: each partition has one leader and several follower replicas, and a write is only considered durable once it has been replicated to the configured minimum number of in-sync replicas. For the order-confirmed topic — the trigger for real money movement — this system configures acks=all and a minimum of two in-sync replicas, trading a small amount of write latency for a strong guarantee that a confirmed order event cannot silently vanish if a single broker fails moments after accepting it.

9.3 Java: Circuit Breaker Around the Commission Service Call

Here is a concrete illustration of the circuit breaker mentioned earlier, protecting the Attribution Engine’s call into the Commission Service so a slow or failing downstream dependency degrades gracefully instead of stalling the whole Kafka consumer group.

CommissionServiceClient.java — resilience with Resilience4j
@Service
public class CommissionServiceClient {

    private final CircuitBreaker circuitBreaker;
    private final RestTemplate restTemplate;

    public CommissionServiceClient(CircuitBreakerRegistry registry, RestTemplate restTemplate) {
        this.circuitBreaker = registry.circuitBreaker("commission-service",
                CircuitBreakerConfig.custom()
                        .failureRateThreshold(50)
                        .waitDurationInOpenState(Duration.ofSeconds(30))
                        .slidingWindowSize(20)
                        .build());
        this.restTemplate = restTemplate;
    }

    public void sendAttributedCredits(String orderId, List<AttributedCredit> credits) {
        Supplier<Void> call = CircuitBreaker.decorateSupplier(circuitBreaker, () -> {
            restTemplate.postForObject(
                    "/internal/commissions/calculate",
                    new CommissionRequest(orderId, credits), Void.class);
            return null;
        });

        try {
            call.get();
        } catch (CallNotPermittedException ex) {
            // Circuit is open; park the event for retry instead of blocking the consumer.
            deadLetterQueue.park(orderId, credits);
        }
    }
}

When the circuit trips open after repeated failures, the Attribution Engine stops sending calls to a clearly unhealthy Commission Service and instead parks the affected events in a dead-letter queue for automatic retry once the circuit closes again, rather than letting a growing backlog of failing calls stall the Kafka consumer group and, with it, every other order waiting behind it in the partition.

10

Security

Because real money flows out of this system, it is a direct target for fraud, and security here is as much about financial integrity as it is about traditional data protection.

10.1 Common Attack Patterns

AttackDescriptionMitigation
Click fraud / click farmsBots or paid workers generate massive fake click volume to inflate a referrer’s apparent reach or trigger volume-based bonuses.Rate limiting per IP and device fingerprint at the API Gateway; bot-detection scoring in the Fraud Detection Service; requiring a minimum session engagement before a click counts toward volume bonuses.
Cookie stuffingA malicious referrer silently drops referral cookies on visitors who never actually saw or clicked their link, hijacking credit for organic sales.Requiring an explicit, logged click event (not just cookie presence) as the source of truth; validating referrer header and click provenance server-side.
Self-referralA referrer buys their own products, or has friends buy, purely to earn commission on money that was going to be spent anyway.Matching billing/account identity against referrer identity; flagging suspiciously repetitive buyer-referrer pairs for manual review.
Last-click hijackingA malicious browser extension or coupon-site script fires a hidden click on its own referral link right before checkout, stealing credit from the genuine influencer.Server-side click validation with referrer-header and timing checks; giving legitimate first-touch referrers partial credit under non-last-touch models reduces the financial incentive for this attack.
Fake account ringsA coordinated group creates many fake customer accounts specifically to generate referral-eligible orders that are later cancelled or refunded after the commission window closes, extracting value without ever intending real purchases.Correlating account creation velocity, shared payment instruments, and shared device signals across accounts; delaying payout eligibility until well past the typical refund window for a given product category.

None of these mitigations work perfectly in isolation, which is exactly why the Fraud Detection Service is designed as a layered, scoring-based system rather than a single hard rule. A referrer flagged by one signal alone — say, a slightly elevated click-to-conversion ratio — might simply be running an unusually effective campaign; it is the combination of multiple independent signals crossing a combined threshold that meaningfully separates genuine high performers from coordinated abuse, and even then, the system defaults to routing borderline cases to human review rather than making an irreversible automated decision to withhold a real referrer’s earned commission.

10.2 Data Protection

  • IP addresses are hashed before storage rather than kept in plaintext, satisfying data minimization principles under regulations like GDPR and India’s DPDP Act 2023.
  • All traffic terminates TLS at the Load Balancer; internal service-to-service traffic uses mutual TLS within the private network.
  • The API Gateway enforces authenticated, scoped API keys for referrer-facing endpoints, so one referrer can never query another referrer’s click or commission data.
  • Payout Service integrates with payment providers using tokenized bank details, never storing raw account numbers in application databases.
📌
What an interviewer may ask

“How would you detect cookie stuffing without blocking legitimate marketing tools?” — A good answer discusses requiring a genuine, timestamped, server-logged click event as the only valid source of attribution credit, cross-referencing referrer headers, and applying anomaly detection on click-to-conversion ratios that are implausibly high for organic behavior.

10.3 Authentication and Authorization Model

Referrers authenticate to their dashboard and API using scoped API keys, issued per referrer account and rotatable on demand if a key is suspected to be compromised. The API Gateway validates these keys on every request and attaches the resolved referrer identity to the request context, so every downstream service can enforce row-level authorization — a referrer’s dashboard query can only ever return that referrer’s own clicks and commissions, enforced both at the API layer and, defensively, again at the database query layer as a second line of defense. Internal service-to-service calls, such as the Attribution Engine calling the Commission Service, use short-lived service tokens issued by an internal identity provider rather than long-lived static credentials, limiting the damage window if a service’s credentials were ever leaked.

10.4 Secrets Management

Database credentials, payment-provider API keys, and signing secrets are never stored in application configuration files or environment variables checked into source control. Instead they live in a dedicated secrets manager, injected into each service’s runtime environment at deploy time and rotated on a schedule, with every access to a secret logged for audit purposes — an important control given that this system’s Payout Service ultimately holds credentials capable of moving real money.

10.5 Input Validation and Compliance Considerations

Every user-controlled input that reaches this system — referral codes, redirect destination parameters, UTM values, dashboard search filters — is validated against a strict allowlist pattern before use, since the Click Tracking Service’s redirect behavior is a classic target for open-redirect attacks, where an attacker crafts a referral link whose destination parameter points somewhere malicious rather than the intended product page. The Referral Link Management Service enforces that a link’s destination must resolve to a pre-registered domain under the marketplace’s own control, closing off that entire attack class at the point of link creation rather than trying to filter it at redirect time. On the compliance side, the commission ledger’s audit trail requirements typically overlap significantly with standard financial recordkeeping regulations, and many marketplaces additionally bring this system’s payout flows within scope of anti-money-laundering checks — verifying referrer identity and flagging unusually large or unusually frequent payouts for manual review before funds are released, in the same way a bank would monitor unusual account activity.

11

Monitoring, Logging & Metrics

Because commission money depends on this pipeline functioning correctly end to end, monitoring here needs to cover both classic system health and business-correctness signals.

11.1 Key Metrics

  • Redirect latency (p50, p95, p99) from the Click Tracking Service — the single most customer-visible metric.
  • Kafka consumer lag on the click-events and order-confirmed topics — rising lag means attribution and commission calculation are falling behind reality.
  • Click-to-conversion rate per referrer — sudden spikes are a strong fraud signal, not just a marketing success signal.
  • Commission calculation error rate — any exception during commission math should page an on-call engineer immediately, since it directly affects money owed.
  • Attribution model distribution — tracking how often each attribution model is applied helps validate configuration correctness across referral programs.

11.2 Logging and Tracing

Every click, touchpoint, and commission calculation carries a correlation ID that flows through Kafka headers, so a single customer journey can be traced end-to-end across every microservice using distributed tracing tools. This is essential for resolving referrer disputes — “why was I not credited for this sale” needs to be answerable by reconstructing the exact touchpoint history and attribution decision, not by guessing.

Common mistake

Teams sometimes only log aggregated metrics and discard raw click events after a short retention period to save storage cost. This backfires the first time a major referrer disputes a commission calculation, because there is no way to prove or disprove what actually happened. Raw touchpoint events should be retained for at least the attribution window plus a dispute-resolution buffer, often 90 days or more.

11.3 Service Level Objectives and Alerting

Turning the metrics above into something actionable means attaching concrete Service Level Objectives (SLOs) and alert thresholds to each one, rather than just dashboarding them for passive viewing.

SLOTargetAlert condition
Redirect availability99.95% monthlyPage on-call if error rate exceeds 0.5% over any 5-minute window.
Redirect latencyp99 under 100msAlert if p99 exceeds 150ms sustained for 10 minutes.
Kafka consumer lag (attribution)Under 60 secondsAlert if lag exceeds 5 minutes, page if it exceeds 30 minutes.
Commission calculation errorsZero tolerancePage immediately on any exception in the commission write path.

These thresholds are deliberately asymmetric — a redirect latency blip pages after ten minutes of sustained degradation, while any commission calculation error pages instantly, reflecting how much more costly a silent financial mistake is compared to a brief, self-recovering latency spike.

12

Deployment & Cloud Architecture

This system maps naturally onto a microservices deployment on any major cloud provider.

  • Containerization: Every service (Click Tracking, Identity Resolution, Attribution Engine, Commission, Payout, Fraud Detection, Reporting) is packaged as an independent container and orchestrated with Kubernetes, allowing each to scale independently based on its own load profile — the Click Tracking Service needs far more replicas than the Payout Service.
  • Regional deployment: For a marketplace operating across multiple geographies, the Click Tracking Service and CDN are deployed regionally close to users to minimize redirect latency, while the authoritative PostgreSQL commission ledger can remain in a primary region with read replicas elsewhere.
  • Blue-green or canary deployments: Because the Attribution Engine directly affects money, changes to attribution logic are deployed via canary rollout with shadow-mode validation — running the new logic in parallel against real traffic and comparing outputs to the existing production logic before fully cutting over.
  • Infrastructure as code: Load Balancer rules, API Gateway routes, Kafka topic configuration, and autoscaling policies are all defined declaratively so environments (staging, production) stay consistent.

12.1 Cost Optimization

The largest cost driver in this system is usually the click-event store, since raw click volume vastly exceeds order volume — a marketplace might see a 50-to-1 or higher ratio of clicks to conversions. A few concrete levers keep this affordable at scale: tiering older click data from Cassandra’s fast storage to cheaper cold object storage once it ages past the maximum attribution window plus a dispute-resolution buffer; compressing raw event payloads before they hit Kafka to reduce both network and storage costs; and using spot or preemptible compute instances for the Attribution Engine’s batch recomputation jobs, which are naturally fault-tolerant and re-runnable, unlike the latency-critical Click Tracking Service which needs guaranteed capacity.

12.2 Observability Tooling in the Deployment Stack

Every service in this architecture ships structured logs, metrics, and distributed traces to a centralized observability stack as a standard part of its deployment, rather than as something bolted on later per service. Metrics are scraped on a standard interval and fed into a time-series store that powers both the dashboards discussed in the monitoring section and the SLO-based alerting rules. Distributed tracing uses a shared correlation ID injected at the API Gateway and propagated through Kafka message headers, so a single customer journey — from the original click through identity resolution, attribution, and commission calculation — can be reconstructed as one trace spanning multiple services and even multiple days, which is invaluable both for debugging production issues and for answering referrer disputes about why a specific commission was or was not paid.

13

Databases, Caching & Load Balancing Choices

13.1 Why PostgreSQL for the Commission Ledger

Commission and payout data needs strong ACID transactional guarantees, foreign-key integrity between orders, referrers, and ledger entries, and support for complex financial queries (reconciliation, tax reporting). A relational database like PostgreSQL is the right tool here, not a NoSQL store.

13.2 Why Cassandra for Click and Touchpoint Events

Click and touchpoint data is write-heavy, append-only, naturally partitioned by visitor ID, and needs to support fast range queries (“give me all touchpoints for this visitor in the last 30 days”). Cassandra’s wide-column model and linear write scalability fit this access pattern far better than a relational database would at this volume.

13.3 Why Redis for Session and Journey Caching

Redis provides sub-millisecond lookups for referral link resolution and cached journey summaries, both of which are read constantly on the hot click path and do not need the durability guarantees of the primary stores.

13.4 Load Balancer Configuration Notes

The Load Balancer in front of the API Gateway uses layer-7 (application-aware) routing so it can make decisions based on URL path — routing /r/* click-redirect traffic to a dedicated, highly-scaled pool of Click Tracking Service instances, separate from the pool serving heavier dashboard and reporting queries, so a burst of reporting traffic can never slow down redirects.

13.5 Schema Design Notes

The Cassandra click-event table is modeled with a partition key of visitor ID and a clustering column of timestamp, which is what makes “give me all touchpoints for this visitor, ordered by time, within the attribution window” a single efficient partition read rather than a scatter-gather query across the whole cluster. The PostgreSQL commission ledger, by contrast, is modeled relationally with foreign keys tying every ledger entry back to an order ID, a referrer ID, and an attribution-run ID, with a unique constraint on order ID plus referrer ID that provides a database-level guarantee against duplicate commission entries even if application-level idempotency checks were ever bypassed by a bug. Indexes on referrer ID and status columns keep the referrer-facing dashboard queries fast without needing to scan the full ledger table.

13.6 Multi-Currency and Tax Handling

Marketplaces operating across multiple countries need the commission ledger to store both the original transaction currency and a normalized reporting currency, using an exchange rate snapshot captured at the time of the order rather than a live rate, so historical commission amounts never silently change value due to later currency fluctuation. Tax handling similarly needs to be resolved and stored at calculation time — whether a commission is treated as taxable income requiring a withholding deduction depends on the referrer’s registered tax jurisdiction, which the Commission Service looks up from the referrer’s profile before finalizing the ledger entry.

Incoming Load BalancerL7 path-based routing Click Tracking Poolmany small fast instances Reporting Poolfewer larger instances Payout Service Poollow volume, high sensitivity /r/* /dashboard/* /api/payouts/*
Fig 13.1 — Path-based Load Balancer routing isolates latency-critical redirect traffic from heavier reporting workloads.
14

APIs & Microservices

14.1 Representative API Surface

EndpointPurpose
GET /r/{shortCode}The click-redirect endpoint; public, extremely high traffic.
POST /api/v1/linksReferrer creates a new referral link, authenticated via API key.
GET /api/v1/dashboard/summaryReferrer dashboard view: clicks, conversions, pending and paid commission.
POST /internal/orders/confirmedInternal event-driven trigger (or event topic) signaling a paid order, consumed by the Attribution Engine.
GET /api/v1/commissions/{referrerId}Detailed, auditable commission ledger for a referrer.
POST /internal/payouts/runTriggers a scheduled payout batch, restricted to internal finance systems.

14.2 Sample Request and Response

Here is what a typical dashboard summary call looks like end to end, illustrating the shape of data referrers actually consume:

GET /api/v1/dashboard/summary — sample response
GET /api/v1/dashboard/summary?range=last_30_days
Authorization: Bearer rk_live_9f3a2b...

{
  "referrerId": "AJAY123",
  "clicks": 18420,
  "conversions": 312,
  "conversionRate": 0.0169,
  "pendingCommission": "48210.50",
  "paidCommission": "192440.00",
  "currency": "INR",
  "topLinks": [
    { "shortCode": "blender9000", "clicks": 4120, "conversions": 61 },
    { "shortCode": "airfryer-x2", "clicks": 3890, "conversions": 54 }
  ]
}

14.3 Error Handling and API Versioning

Every public endpoint returns a consistent error envelope rather than raw stack traces or provider-specific error formats, which matters a great deal for a platform with thousands of independent referrer integrations relying on predictable behavior.

StatusMeaningTypical cause
400Bad RequestMalformed link creation payload, invalid date range on a dashboard query.
401UnauthorizedMissing or invalid API key.
403ForbiddenValid key, but attempting to access another referrer’s data.
404Not FoundReferral short code does not exist or has been deactivated.
429Too Many RequestsReferrer has exceeded their API Gateway rate limit.
503Service UnavailableA downstream circuit breaker is open; the Gateway signals a temporary, retryable condition.

The public API is versioned in the URL path (/api/v1/...), and breaking changes are only ever introduced in a new version, with the previous version kept live and supported for a documented deprecation window — often six to twelve months — since referrer-built integrations are external code the platform team does not control and cannot force to upgrade on a moment’s notice.

14.4 Why Microservices Here, Not a Monolith

Each service in this system has a genuinely different scaling profile, latency requirement, and failure blast radius. Click Tracking must be extremely fast and horizontally scaled to enormous instance counts; Payout Service runs rarely and needs strong consistency more than raw throughput; Fraud Detection may run expensive machine learning models. Splitting these into independent services lets each be scaled, deployed, and even written in the most appropriate technology independently, and — critically — lets a Fraud Detection bug or slowdown never risk breaking the customer-facing redirect path.

📌
What an interviewer may ask

“Would you ever combine Identity Resolution and Attribution Engine into one service?” — A thoughtful answer weighs the coupling: both operate on the same journey data and could share a data store, but Attribution only needs to run at conversion time while Identity Resolution runs continuously on every click, so keeping them separate lets each scale to its own workload shape independently.

15

Design Patterns & Anti-Patterns

15.1 Patterns Used

Pattern

Event Sourcing

Click and touchpoint data is stored as an immutable sequence of events, and current state (a journey, a commission balance) is derived by replaying or aggregating those events, rather than being mutated directly.

Pattern

CQRS

Writes (click ingestion) and reads (referrer dashboard analytics) use entirely different data paths — the write path is optimized for throughput, the read path for the Data Warehouse is optimized for flexible aggregation queries.

Pattern

Strategy Pattern

The Attribution Engine’s model selection (first-touch, last-touch, linear, and so on) is implemented as a swappable strategy, letting the business change attribution rules per program without modifying core engine code.

Pattern

Circuit Breaker

Protects the Attribution Engine and Commission Service from cascading failures when a downstream dependency slows down.

Pattern

Idempotent Consumer

Every event consumer is designed to safely process the same message more than once without corrupting commission totals.

Pattern

Transactional Outbox

When the Commission Service writes a ledger entry to PostgreSQL, it writes a corresponding outbound event to an outbox table within the same database transaction, and a separate relay process publishes that event to Kafka afterward. This avoids the classic dual-write problem where a database write succeeds but the following message publish fails, leaving the system in an inconsistent state with no record that anything went wrong.

Pattern

Saga Pattern

The Payout Service coordinates its multi-step process — reserve funds, call the payment provider, confirm transfer, update ledger status — as a saga with an explicit compensating action defined for each step, so a failure partway through, such as a payment provider timeout after funds were already reserved, triggers a clean rollback of the reservation instead of leaving money in an ambiguous, unreconciled state.

15.2 Anti-Patterns to Avoid

🚫
Anti-pattern: last-click logic in checkout

Baking last-click logic directly into checkout code makes it impossible to evolve the attribution model without a risky checkout-flow change, and scatters business logic that should live in one place across a critical, high-stakes part of the codebase that engineers are naturally reluctant to touch.

🚫
Anti-pattern: synchronous fraud checks on the click path

Even a “quick” fraud check on the hot click path can turn a 20ms redirect into a 2-second one under load, and a single slow downstream dependency can take down the entire referral program’s customer-facing experience.

🚫
Anti-pattern: mutating commission records in place

Destroys the audit trail needed for financial disputes and regulatory compliance, and makes it effectively impossible to answer the question “what did we actually pay this referrer, and why” months after the fact.

🚫
Anti-pattern: cookies as the sole identity signal

Ignores cross-device behavior entirely and systematically undercounts legitimate multi-touch journeys, quietly biasing the whole system toward whichever referrer happens to be present in the final browser session regardless of who did the real persuasive work.

🚫
Anti-pattern: attribution as a side effect of the order write

Coupling attribution logic directly inside the order database transaction means a slow or buggy attribution calculation can block or fail an otherwise perfectly valid order, conflating two concerns — “did the sale happen” and “who gets credit for it” — that should be allowed to fail independently of each other.

16

Best Practices & Common Mistakes

16.1 Best Practices

Best practice

Store touchpoints immutably

Always store raw touchpoint events immutably, and compute attribution as a derived, replayable function — never as a side effect baked into the write path.

Best practice

BigDecimal, not double

Use BigDecimal or fixed-point arithmetic for every commission calculation, never floating point.

Best practice

Separate fast and slow paths

Separate the “fast path” (click redirect) from the “slow path” (attribution, commission, fraud scoring) with an asynchronous event backbone so the customer experience is never coupled to attribution complexity.

Best practice

Version attribution rules

Version your attribution rules, so historical commission calculations remain explainable even after the business changes its model.

Best practice

Dashboards from warehouse

Build referrer-facing dashboards from a read-optimized Data Warehouse copy, never by querying the live transactional stores directly.

Best practice

Idempotency everywhere money moves

Design for idempotency everywhere money is involved — duplicate event delivery is a “when,” not an “if,” in any distributed system built on message queues.

16.2 Testing Strategy

Because commission math directly affects real payouts, this system leans heavily on a few specific testing practices beyond standard unit tests. Attribution model implementations are tested with property-based tests that assert invariants regardless of input — for example, “the sum of all attributed shares for a journey must always equal exactly 1.0, never more, never less” — which catches rounding and edge-case bugs that example-based tests alone tend to miss. The full pipeline, from a synthetic click event through to a commission ledger entry, is covered by end-to-end integration tests running against real (test-environment) Kafka and database instances rather than mocks, since the ordering and partitioning guarantees that make this system correct are exactly the kind of behavior mocks tend to hide bugs in. Finally, any change to attribution logic is validated in shadow mode against a replay of real historical journeys before being trusted with live commission calculations, comparing old-model and new-model outputs side by side for a statistically meaningful sample of orders.

16.3 Common Mistakes

  • Under-provisioning the Click Tracking Service for flash-sale traffic spikes, causing slow redirects exactly when referral traffic (and revenue) is highest.
  • Discarding raw click data too early, leaving no way to resolve referrer disputes months later.
  • Ignoring refunds in the commission pipeline, resulting in referrers being paid for orders that were later cancelled.
  • Applying fraud detection only at conversion time, missing the chance to filter fraudulent clicks before they ever pollute the touchpoint history.
17

Real-World / Industry Examples

The abstract architecture described throughout this tutorial is not a purely theoretical exercise — every major marketplace and affiliate network running a referral program today has converged on roughly the same set of building blocks, even though the specific technology choices, attribution windows, and commission structures differ meaningfully based on each business’s own transaction patterns, fraud exposure, and customer behavior. The examples below illustrate how the same underlying architectural principles get adapted to genuinely different business contexts.

E-commerce

Amazon Associates

One of the earliest large-scale affiliate programs, historically using a last-touch, 24-hour cookie model for most product categories — prioritizing simplicity and fraud resistance at massive scale over more generous multi-touch crediting. The short window reflects a deliberate trade-off: at Amazon’s transaction volume, even a small percentage increase in fraudulent or disputed attribution translates into enormous absolute cost, so simplicity and short windows reduce both fraud surface and reconciliation complexity.

Ride-sharing

Uber’s Referral Program

Uses a direct code-and-signup attribution model rather than long browsing-journey attribution, since the “conversion” (first ride) typically happens quickly after signup — showing how the attribution window and model should match the actual customer behavior of the specific business. A ride-hailing referral rarely involves a multi-week research journey the way a big-ticket electronics purchase might, so the engineering investment in sophisticated multi-touch modeling would deliver little practical benefit relative to its cost.

Platform

Impact.com and similar platforms

Third-party affiliate platforms that many marketplaces plug into offer configurable multi-touch attribution models (linear, time-decay, position-based) as a core product feature, reflecting how central flexible attribution has become to modern affiliate marketing. These platforms typically expose attribution model selection as a per-program configuration rather than a code-level decision, echoing the Strategy pattern design discussed earlier in this tutorial.

Network

Rakuten Advertising

Operates cross-device identity resolution and multi-touch attribution at large scale across thousands of merchants, illustrating the identity-graph and event-backbone architecture described in this tutorial applied across an entire affiliate network rather than a single marketplace. Operating across many independent merchants also means their identity resolution has to work without the benefit of a single shared login system, leaning more heavily on probabilistic device signals than a single-marketplace system would need to.

Cashback

Cashback and Coupon Aggregator Apps

Cashback apps sit at a structurally interesting position in the attribution journey: they are frequently the very last touchpoint before checkout, since users often open them right before paying specifically to find a discount code. This is exactly the scenario that makes pure last-click attribution controversial in the industry — it can systematically reward the coupon app that intercepted an already-decided purchase over the content creator who did the actual persuasion work days earlier, which is a major reason many large programs have moved toward position-based or time-decay models instead.

Internal

Employee & Customer Referral Programs

Many marketplaces also run an internal referral program where existing customers refer friends directly, typically using a simple unique code rather than a trackable link, with attribution resolved at account signup rather than through browsing-journey reconstruction. This is a useful contrast case: because the referral relationship is captured explicitly at signup, this program flavor needs almost none of the click-tracking or identity-resolution machinery this tutorial focuses on, showing how the right architecture always depends on the specific shape of the referral relationship being modeled.

18

Appendix: Sample Referral Program Configuration

To make the abstract discussion of attribution models, windows, and commission rates concrete, here is what a real program configuration record might look like in the Referral Link Management Service, and a short walkthrough of how each field is actually used elsewhere in the pipeline.

program-config.json — a real referral program record
{
  "programId": "influencer-program-2026",
  "attributionModel": "POSITION_BASED",
  "attributionWindowDays": 21,
  "defaultCommissionPercent": 0.08,
  "maxCommissionPerOrder": 5000.00,
  "minimumOrderValue": 200.00,
  "eligibleCategories": ["electronics", "home-appliances"],
  "fraudScoreThreshold": 70,
  "payoutSchedule": "MONTHLY",
  "currency": "INR"
}

The attributionModel and attributionWindowDays fields feed directly into the Attribution Engine’s strategy selection and eligibility filtering, described in section 5.4. The defaultCommissionPercent and maxCommissionPerOrder fields are read by the Commission Calculation Service during the ledger-writing step shown in section 5.6, while minimumOrderValue lets the business exclude very low-value transactions from earning any commission at all, protecting margin on small orders where an eight percent commission would barely cover the platform’s own transaction costs. The fraudScoreThreshold ties directly into the Fraud Detection Service’s scoring pipeline from section 5.7, and eligibleCategories lets a program restrict itself to specific product categories rather than the whole marketplace catalog — useful, for example, when an electronics-focused influencer program should not accidentally pay out commission on unrelated grocery orders that happened to occur within the same attribution window. Because every one of these fields is data, not code, launching a brand-new referral program with entirely different rules requires no engineering deployment at all — just a new configuration record, which is precisely the flexibility a hardcoded, checkout-embedded last-click system could never offer.

19

Glossary of Terms

TermMeaning
Referral codeA short unique identifier tied to a specific referrer, embedded in a referral link.
TouchpointAny recorded interaction a customer had that could plausibly influence a future purchase.
Attribution windowThe maximum allowed time between a touchpoint and a purchase for that touchpoint to still receive credit.
Attribution modelThe mathematical rule used to split credit for a sale among multiple touchpoints in a journey.
Identity resolutionThe process of merging anonymous, cookie-based sessions into one customer identity, typically triggered by a login event.
Deferred deep linkingA technique for passing referral context through an app-store install, since a native app cannot read a mobile browser’s cookies.
Cookie stuffingA fraud technique where referral cookies are silently dropped on visitors who never actually clicked or saw the referral link.
ClawbackA negative, compensating commission ledger entry issued when a previously-commissioned order is refunded or cancelled.
Idempotent consumerA message consumer designed so that processing the same event twice never produces an incorrect or duplicated result.
Shadow modeRunning new logic in parallel with production logic on real traffic, without affecting real outcomes, purely to validate correctness before cutover.
20

FAQ, Summary & Key Takeaways

20.1 Frequently Asked Questions

Q

Why not just use the last click for everything? It is so much simpler.

Last-click attribution is simple and fraud-resistant, but it systematically undervalues top-of-funnel referrers who introduce customers to a product days or weeks before purchase, which over time discourages exactly the kind of high-quality content marketing many marketplaces want to encourage.

Q

How do you handle a customer who clears cookies between touchpoints?

Cookie clearing genuinely breaks anonymous identity continuity. The system mitigates this through login-based identity resolution — once a customer logs in on any device, prior anonymous sessions matching device or network signals can sometimes be probabilistically merged, but a fully cookie-cleared, never-logged-in journey will unavoidably lose some touchpoint history. This is a known, accepted limitation across the industry.

Q

What happens if two referrers both claim the same click?

A single click event is tied to exactly one referral code at capture time — there is no ambiguity there. The more common real scenario is multiple different touchpoints from different referrers across one journey, which is precisely what multi-touch attribution models are designed to split fairly.

Q

How do refunds affect already-paid commissions?

A refund generates a compensating negative ledger entry (a clawback) rather than deleting the original commission record, preserving the audit trail. If the commission was already paid out, the clawback is typically deducted from the referrer’s next payout cycle rather than demanding an immediate repayment.

Q

Can the attribution model differ per referral program?

Yes — since the Attribution Engine implements attribution models as a swappable strategy, different referral programs (an influencer program versus an employee-referral program versus a cashback-app integration) can each be configured with the model that best fits their business goal.

Q

How do you prevent a referrer from seeing another referrer’s data?

Authorization is enforced at two layers: the API Gateway resolves every request to exactly one authenticated referrer identity from their API key or dashboard session, and every downstream query is additionally scoped to that referrer ID at the database layer as a defensive second check, so even a bug in one layer alone cannot leak cross-referrer data.

Q

Why is the Click Tracking Service kept so deliberately simple?

Every additional piece of logic on the click-redirect path — a synchronous fraud check, a database write instead of an async event, an extra network hop — adds latency that is multiplied across the highest-traffic endpoint in the entire system. Keeping it thin and pushing all complexity into asynchronous downstream services is one of the most important architectural decisions in this design.

Q

How would you migrate from a last-click system to full multi-touch attribution without disrupting existing referrers?

Run the new multi-touch Attribution Engine in shadow mode first — computing attribution in parallel with the existing last-click logic without actually changing payouts — for a full attribution-window cycle, comparing the outputs and validating that overall commission totals move in expected, explainable directions before cutting production traffic over to the new model.

Q

What is the single most important design decision in this entire system?

Separating the latency-critical click-redirect path from every other piece of business logic through an asynchronous event backbone. Almost every other design choice in this tutorial — Kafka as the backbone, stateless services, eventual consistency for click data, strong consistency only for money — flows directly from protecting that one hot path.

Q

How would this design change for a marketplace with far smaller traffic, say a few hundred orders a day?

At that scale, much of this architecture’s complexity — Kafka, Cassandra, a dedicated Identity Resolution Service — would be over-engineering. A simpler design with a single relational database, a scheduled batch job computing attribution nightly, and a basic cookie-based click log would likely serve the business better, with room to evolve toward this fuller architecture only once real traffic and fraud pressure justify the added operational cost.

Q

How do you handle a referrer who disputes a specific commission calculation months after the fact?

Because every touchpoint, attribution decision, and commission ledger entry is stored immutably with a correlation ID tying them together, a support engineer can reconstruct the exact customer journey, the exact attribution model version that was active at the time, and the exact math applied — turning what could be a “trust me” conversation into a fully reproducible, evidence-backed explanation. This is precisely why the design insists on immutability and full historical retention rather than discarding raw data once a commission has been paid: disputes are not a rare edge case in an affiliate program handling real money, they are a routine, expected part of operating one, and the system needs to be built to answer them confidently from day one rather than scrambling to reconstruct history after the fact.

20.2 Summary

Designing an affiliate and referral attribution system is ultimately an exercise in building a trustworthy, auditable record of causality — deciding, fairly and defensibly, who deserves credit when a sale happens after a customer’s attention was captured by several different sources over days or weeks. The architecture in this tutorial makes that possible by separating concerns cleanly: a thin, extremely fast Click Tracking Service sitting behind a Load Balancer and API Gateway captures raw signal without slowing anyone down; an asynchronous event backbone built on Kafka decouples that fast path from every piece of downstream complexity; an Identity Resolution Service stitches fragmented sessions into coherent journeys; an Attribution Engine applies a configurable, swappable mathematical model to split credit; and a Commission Service, backed by a strongly consistent relational store, turns that credit into real, auditable money. Every one of these pieces exists because a simpler, more naive design — cookie-only last-click attribution baked into checkout code — breaks down under real-world scale, real-world fraud incentives, and real-world customer behavior that spans devices, sessions, and weeks. Understanding why each component exists, not just what it does, is what separates a system that merely works from one that survives contact with production traffic, hostile actors, and finance-team audits.

Key Takeaways

  • Attribution is fundamentally a data-modeling problem: capture every touchpoint immutably, and treat “who gets credit” as a derived calculation, not a hardcoded rule.
  • The click-redirect path is latency-critical and must be decoupled from all downstream attribution, fraud, and commission complexity through an asynchronous event backbone.
  • Every service in this architecture sits behind a Load Balancer and API Gateway, which together handle traffic distribution, authentication, and routing before any business logic executes.
  • Different attribution models (first-touch, last-touch, linear, time-decay, position-based) represent genuine business trade-offs, not just implementation details.
  • Because real money is at stake, immutability, idempotency, and auditability are not optional extras — they are core correctness requirements of the system.
  • Fraud detection must be layered throughout the pipeline, not bolted on only at the final commission-calculation step.
💡
Final thought

The best attribution systems are ones you never notice: referrers get paid fairly and on time, disputes are resolved with data rather than opinion, and the customer clicking a link never sees anything but a fast, invisible redirect to the product they wanted. All of the architectural complexity in this tutorial exists in service of that simple, quiet outcome.