Historical Project Outcomes at a Glance
- Administrative Time: Automated financial synchronization saved the accounting operation an estimated 20+ hours per week of manual work.
- Omnichannel Consolidation: Customer self-service and field-sales activity were brought into a shared operational and financial workflow.
- Pricing Efficiency: The hybrid pricing model reduced the pricing-data footprint by 95%, with storefront rendering recorded at under 300ms during the engagement.
- Infrastructure: The self-hosted AWS deployment recorded 99.9% uptime during the project period.
These are historical outcomes recorded during the engagement. They were not re-benchmarked during the later portfolio evidence review.
1. Executive Summary: Commerce Was Only One Part of the System
Wholesale commerce has a structural problem that conventional storefronts rarely need to solve: the commercial relationship changes the transaction.
Two customers can buy the same product on the same day and legitimately receive different prices. A sales representative may place an order while standing inside a customer's shop. That same representative may also collect cash against an older invoice. An administrator then has to determine what was ordered, what has been paid, what still needs allocation, what should be fulfilled, and what Sage 50 should ultimately record.
Dairy Farm Products was dealing with exactly this kind of operational complexity.
The existing process connected several worlds imperfectly:
- customer self-service ordering,
- field-sales activity,
- negotiated wholesale pricing,
- internal order approval,
- fulfilment and inventory operations,
- cash and card receipts,
- and Sage 50 accounting.
The expensive part was not any single screen. It was the human coordination required between them.
An online order could still become an accounting-entry task. A cash collection recorded in the field could still require office reconciliation. Customer-specific pricing could exist as scattered exceptions rather than a predictable resolution rule.
The engineering objective was therefore broader than rebuilding an e-commerce frontend.
I built DFP as a shared B2B operations platform where commerce, pricing, order operations, payments, and accounting synchronization work against one application model.
The customer-facing side begins with a conventional enough experience: authenticated wholesale buyers browse products, filter the catalogue, see pricing relevant to their account, and place orders.

Behind that interface is the larger system: customer identity determines pricing context, orders enter a shared operational workflow, administrators review and fulfil them, financial receipts are allocated against invoices, and Sage synchronization runs asynchronously behind those actions.
That shared lifecycle is the core of the project.
2. The Operational Bottlenecks
The application needed to make the customer experience feel straightforward while absorbing several less-visible B2B constraints behind it.
Constraint 1: Customer-Specific Pricing Without a Combinatorial Data Model
Wholesale pricing is relationship-driven.
A master product may have a standard price. A category of customers may receive a negotiated group price. A specific account may have an individual contract exception that overrides both.
A naïve implementation can model that by storing a price for every customer-product pairing.
At illustrative scale, 5,000 products across 2,000 customers would already imply 10 million potential baseline combinations before considering historical changes, tiering, or exceptions. Those figures illustrate the shape of the problem rather than DFP's actual customer or product counts.
The architecture needed to preserve negotiated flexibility without turning every product/account combination into duplicated pricing data.
Constraint 2: Sage 50 Was a Financial Authority Without a Modern Event Model
Sage 50 remained central to the financial workflow, but it did not provide native webhooks that the application could simply subscribe to.
That meant synchronization had to deal with two uncomfortable realities:
- the accounting platform could not be assumed to respond in real time, and
- temporary Sage availability should not prevent customers, sales representatives, or administrators from using the operational application.
The system therefore needed an explicit synchronization boundary rather than pretending the accounting package behaved like an always-available internal service.
The administrative customer view exposes one practical consequence of that separation. Application access state and accounting synchronization state are different concerns, and the interface presents them independently.
An account can be verified or locked while its Sage synchronization state is inspected separately.

This distinction matters operationally. An administrator should not have to infer whether a customer-access problem is actually an accounting-sync problem, or vice versa.
Constraint 3: One Sale Could Begin in Several Places
Wholesale ordering did not originate from one channel.
A customer could order through the self-service application. A sales representative could create an order for a customer in the field. Physical cash could be collected during a visit. Card payments could arrive through Stripe.
Those paths eventually had to converge into the same administrative and financial state.
Without that convergence, "omnichannel" simply means several places where inconsistent records can be created.
The order-management surface shows how the application made origin and operational state explicit. Administrators can see approval status, order source, sales rounds, placement and delivery dates, item details, and fulfilment actions from one workflow.

That gives the system a clear transition from sales activity into internal operations rather than treating an order as finished the moment it reaches the database.
3. Architecture Choice: Keep the Business Transaction Close Together
The platform was implemented as a strongly typed modular monolith deployed on self-hosted AWS ECS.
That architecture was deliberate.
Pricing, customer state, order approval, payment allocation, and synchronization all participate in the same business transaction graph. Splitting those responsibilities prematurely across multiple network services would have increased coordination overhead around the exact operations that benefited from shared validation and shared transactional state.
The modular-monolith approach kept those boundaries explicit in code without forcing every internal operation through a distributed network call.
The core stack included:
- TypeScript for application-level type safety,
- Zod for runtime validation,
- tRPC for strongly typed application APIs,
- PostgreSQL / Prisma for operational persistence,
- Redis / BullMQ for asynchronous synchronization and retryable jobs,
- Stripe for card-payment flows,
- Sage 50 as part of the accounting workflow,
- and AWS ECS for self-hosted application infrastructure.
The result was one operational application with clear domain modules rather than several independent products that happened to exchange data.
4. Module A: Pricing as a Resolution Problem
The pricing engine was built around precedence rather than duplication.
Three layers describe the available price sources:
-
Base Price
The default price attached to the master product. -
Tiered Group Price
A price or adjustment associated with a wholesale customer group. -
Customer Override
A narrow account-specific exception for a negotiated contract.
The important detail is the resolution order.
The system begins with the most specific rule:
Customer Override → Tiered Group Price → Base Price
If a customer-specific override exists, it wins. If it does not, the engine checks the customer's wholesale group. If neither exists, the master-product price remains the fallback.
This structure had several operational consequences.
A customer-specific contract remains an exception instead of becoming a duplicate copy of the whole catalogue.
A tier-level change can apply across many accounts without creating the same rule repeatedly.
A base-price update still provides a common fallback.
Most importantly, the same resolution logic can be reused wherever an order originates. A price should not change merely because a sales representative entered the order instead of the customer.
The project recorded a 95% reduction in pricing-data footprint from this hybrid model.
The Product Master Behind the Pricing Engine
Dynamic pricing still needs a reliable product baseline.
The inventory view exposes the common data that pricing rules build on: product taxonomy, producer information, base price, stock, and Sage state.

The product editor then gives administrators control over the master data behind that inventory record: images, variants, size, taxonomy, producer, base price, and tax configuration.

This is an important part of the architecture because negotiated pricing only remains maintainable when the master product itself is authoritative.
Taxonomy, product identity, tax treatment, variants, and base pricing belong to the product model. Contract pricing then adds context on top of that model rather than redefining the product for every customer.
5. Module B: Treat Sage Synchronization as Asynchronous Work
The Sage integration could not rely on a webhook-driven model, so I designed the synchronization path around eventual consistency.
Operational actions happen inside the application first.
Approved orders and payment allocations become platform state. Work that needs to reach Sage is then moved into a Redis / BullMQ queue and processed outside the user's request path.
That separation matters.
A customer placing an order should not be forced to wait for an accounting package to acknowledge a remote operation. An administrator allocating a receipt should not lose the allocation simply because Sage experiences a temporary failure.
The synchronization layer therefore treats remote accounting work as retryable background work.
Poll, Compare, Apply Only What Changed
The reverse synchronization path used snapshot comparison rather than indiscriminately replacing local state.
Background workers pull the relevant Sage state, compare it against PostgreSQL, and identify the records that have actually changed.
That creates a narrower reconciliation surface.
Instead of treating every polling cycle as a complete replacement operation, the application can apply the differences that matter.
This is also where retry behavior becomes important.
If Sage throttles, becomes temporarily unavailable, or rejects a request, the queue can retain pending work and retry with exponential backoff. The user-facing application continues operating against its own valid state while the accounting integration catches up.
That is the practical value of eventual consistency here: temporary integration failure becomes delayed synchronization rather than failed business operation.
6. Module C: One Ledger-Reconciliation Path for Cash and Card Payments
Orders were only one side of the financial workflow.
Payments could also enter the system through different channels.
A field representative might collect physical cash against an outstanding customer balance.
A customer might pay by card through Stripe.
From the application's perspective, those receipts still need to answer the same accounting questions:
- Which customer does this receipt belong to?
- Which invoice or invoices should it settle?
- Has the full amount been allocated?
- Is any portion still unallocated?
- Has the resulting state synchronized successfully?
The platform therefore brings those payment sources into one administrative allocation workflow.

The screen exposes receipt reference, amount, unallocated balance, existing allocations, synchronization state, and received date.
That separation between receipt and allocation is operationally important.
Receiving money does not by itself determine which accounting obligation has been settled. Allocation is the step that turns a receipt into reconciled financial state.
Once the administrator performs that allocation, the same asynchronous synchronization path can move the resulting accounting changes toward Sage 50.
This gives both field cash and online card payments a common route into the ledger instead of maintaining separate reconciliation processes for each channel.
7. The Operational Command Surface
The backend architecture mattered because it supported day-to-day operational decisions.
DFP was not designed as a collection of automation jobs hidden behind an online shop. The administrative application exposes the state required to operate the business when normal and exceptional cases occur.
Order Review and Fulfilment
An incoming order carries more than line items.
Administrators can inspect:
- approval state,
- order status,
- source,
- associated sales context,
- sales round,
- placement date,
- delivery date,
- item detail,
- and fulfilment actions.
Order pack lists and round pick lists connect the digital order record to physical warehouse activity.
That matters because an e-commerce order is not complete operationally when the customer presses Submit. It still has to become something that can be picked, packed, delivered, invoiced, and reconciled.
Customer and Account State
Customer administration separates operational access from accounting synchronization.
Verification and locking controls manage application access. Sage synchronization state communicates a different concern: whether accounting data is aligned.
Keeping those states separate prevents an integration issue from being confused with an account-permission issue.
The platform also included internal member and role administration, giving the operations team a controlled way to distinguish customer, sales-representative, and internal access contexts.
Product and Inventory Administration
The inventory and product-management surfaces centralize the product information used across commerce and operations:
- category and subcategory,
- producer,
- variants,
- stock,
- base price,
- tax configuration,
- product imagery,
- and synchronization state.
That centralization is a prerequisite for reliable pricing.
A customer-specific price rule is only useful when every sales channel resolves against the same underlying product identity.
Financial Exceptions Remain Visible
The payment-allocation interface also reflects a broader principle in the system: automation should not hide unresolved state.
Unallocated amounts remain visible.
Synchronization status remains visible.
Receipts can be inspected before the system considers them fully reconciled.
That makes the administrative UI the control surface for the exceptions that cannot or should not be resolved automatically.
8. From Order to Reconciled Ledger
The easiest way to understand the system is to follow one transaction across its boundaries.
Step 1: A Commercial Action Begins
A customer signs in and creates an order through the self-service storefront, or a sales representative creates the order while working with that customer.
Both paths enter the same operational domain.
Step 2: Pricing Resolves From Customer Context
For every relevant item, the pricing engine evaluates the most-specific rule available.
A customer override wins first.
If no override exists, the customer's wholesale group can supply the price.
Otherwise the master-product base price remains the fallback.
The result is therefore determined by commercial context rather than by which interface created the order.
Step 3: Administration Reviews the Order
The order enters the administrative workflow with its source and operational metadata intact.
Staff can review approval state, delivery timing, sales-round information, and item detail before moving it toward fulfilment.
Step 4: Approved Financial State Enters the Sync Pipeline
Once the order reaches the appropriate state, invoice-related work can be queued for Sage synchronization.
The user interaction does not need to remain blocked while the accounting integration completes.
PostgreSQL retains operational state; Redis/BullMQ carries synchronization work to the background worker.
Step 5: Sage and the Application Reconcile
The synchronization layer exchanges the required accounting information with Sage.
Polling and snapshot comparison provide the reverse path, identifying changes that need to be reconciled into the application.
Temporary failures remain retryable.
Step 6: Payment Arrives
A customer may pay through Stripe.
A field representative may record physical cash.
Both receipts enter the same financial model rather than creating separate accounting silos.
Step 7: The Receipt Is Allocated
An administrator assigns the incoming amount against the relevant invoice or invoices.
Any remaining unallocated amount stays visible.
Step 8: The Ledger Catches Up
The allocation becomes part of the state synchronized with Sage.
At that point, a commercial action that may have started on a customer's phone or in a field-sales visit has moved through pricing, order operations, fulfilment context, receipt allocation, and accounting reconciliation inside one connected system.
That lifecycle is the real scope of DFP.
The project is an e-commerce platform at the customer edge, but operationally it is a bridge between sales activity and financial truth.
9. Why These Architecture Decisions Mattered
Several engineering decisions were useful because they aligned with the operational problem rather than because they were fashionable technologies.
A Modular Monolith Kept Business Rules Close
Pricing, ordering, allocation, and customer state share domain concepts.
Keeping those modules inside one application boundary reduced unnecessary coordination between tightly related operations while still allowing the codebase to remain modular.
Strong Typing Reduced Cross-Surface Drift
TypeScript, Zod, and tRPC helped the same business concepts travel through frontend forms, application APIs, validation, and administrative workflows without redefining contracts independently at every boundary.
For a system with customer-specific pricing and financial state, that consistency matters more than it would for a simple content site.
Queues Isolated Fragile External Work
BullMQ and Redis allowed Sage synchronization to live outside synchronous customer and admin requests.
That gives the system a place to model retries, temporary failures, and delayed processing explicitly.
The UI Exposed Operational State Instead of Hiding It
Approval state, Sage synchronization state, unallocated payments, account locks, stock, and fulfilment controls remain visible to the people responsible for the operation.
Automation handles repeatable work. The application still gives humans a clear surface for decisions and exceptions.
10. Historical Business Impact
During the engagement, the platform recorded several concrete outcomes.
20+ Hours per Week of Manual Work Removed
Automating the invoice and financial synchronization lifecycle reduced the amount of accounting work that previously depended on manual transfer between systems.
The project estimate was 20+ hours per week saved across the operation.
95% Smaller Pricing-Data Footprint
The hierarchical pricing model avoided representing every possible customer-product relationship as a separate baseline record.
The resulting design reduced the pricing-data footprint by 95% while retaining group pricing and account-specific contract exceptions.
Under-300ms Storefront Rendering
Despite resolving customer-specific prices at request time, optimized application and database behavior recorded storefront rendering at under 300ms during the project.
99.9% Infrastructure Uptime
The containerized AWS deployment recorded 99.9% uptime during the engagement.
These figures describe the historical project outcome. They should not be interpreted as a fresh benchmark of the current production environment.
11. What This Project Demonstrates
DFP is useful in my portfolio because the difficult part was not any individual framework or integration.
The challenge was turning several partially connected business processes into one coherent system.
The application had to understand:
- who the customer was,
- which price they should receive,
- where an order originated,
- whether it had been approved,
- how it should move into fulfilment,
- which money had been received,
- how that money should be allocated,
- and how the final financial state should reconcile with Sage 50.
That required work across the frontend, backend, data model, integrations, background processing, and infrastructure.
I owned that system end to end.
The broader engineering pattern is applicable well beyond wholesale distribution: when an organization has people manually moving the same business state between customer-facing software, internal operations, payments, and a legacy system of record, the real opportunity is to design the workflow as one system rather than automate each screen independently.
View the concise DFP project overview for the high-level ordering, pricing, and accounting architecture.
If an existing business application still depends on staff manually reconciling customer activity with accounting, CRM, ERP, or other legacy systems, let's discuss the system.
