The checkout application is built on top of FeathersJS with a minimal setup using the command
npx create-feathers-app checkout. The preferred tech stack includes:
- TypeScript
- Express as the HTTP framework
- REST API style
- Yarn as the package manager
- TypeBox for schema modeling and validation
This repository contains only the backend—no client application is included. For this minimal setup, no database was chosen. All source code can be found at
/srcfolder, and all tests at/test.
- Salesforce Basket context is non-persistent: SF may drop cart state between calls. We must design to rehydrate or replay required context on demand.
- Security: Requests should be validated using JWT Token (Bearer JWT), SF also uses JWT Token. We have to manage both within our app.
- Experience API: Exposing REST/JSON end-points for
/checkoutflow. - SalesforceAdapter:
SalesforceCartClientan interface that encapsulates all calls to Salesforce. It hides SF API versions and implements retry/backoff and corrective rehydration logic. - Audit & Observability: In a real world application, we would use
SentryorDataDogto store this data. For now, we are gonna stick with the built inlog-error.tshook to get this info.
Below is the formal specification for the core abstractions that must be implemented.
All implementation must follow SOLID principles, especially:
- Single Responsibility: Each module handles only one responsibility.
- Open/Closed: Services should be extendable without modifying core logic.
- Liskov Substitution: Abstractions should be replaceable with mocks/fakes.
- Interface Segregation: Keep interfaces small and purpose-built.
- Dependency Inversion: Higher-level services depend on abstractions, not concrete HTTP clients.
All services must follow the folder convention:
/src/services/<serviceName>/
The base example folder is:
/src/services/shoppers/
Purpose (SRP):
A dedicated client responsible solely for communicating with Salesforce APIs.
No business logic, no Feathers dependencies.
Location:
/src/services/salesforceCartClient/salesforce.class.ts
Required Public Methods:
Each method must be async and return parsed JSON.
interface SalesforceCartClient {
getAccessToken(organizationId: string): Promise<{ access_token: string; refresh_token: string }>;
getBaskets(organizationId: string, basketId: string): Promise<Basket>;
deleteBasketItems(organizationId: string, basketId: string, itemId: string): Promise<Basket>;
patchBasketItems(organizationId: string, basketId: string,itemId: string, itemObject: Record<string, any>): Promise<Basket>;
placeOrder(organizationId: string, basketId: string): Promise<SubmittedOrder>;
}- Use dependency injection for HTTP client (DIP).
- No Feathers imports.
- No state stored inside the class beyond constructor DI.
- Error handling must normalize SF API errors into Feathers-ready exceptions.
Purpose (SRP):
Implements business operations for checkout, using the SalesforceCartClient underneath.
Location:
Generated via Feathers CLI, then extended:
npx feathers generate service
Configuration required:
- Service name:
checkout - Registration path:
/checkout - Authentication:
yes - Database:
custom service - Schema:
TypeBox
The generated folder will be:
/src/services/checkout/
These methods extend the Feathers service class.
They must not directly call Salesforce APIs; instead they must rely solely on the SalesforceCartClient abstraction (DIP).
- Uses SalesforceCartClient.deleteBasketItems
- Returns updated Basket JSON
- No UI logic—pure data operation
- Uses SalesforceCartClient.patchBasketItems
- Returns updated Basket JSON
- Uses SalesforceCartClient.placeOrder
- Returns the final order JSON
- Checkout service must accept SalesforceCartClient in its constructor (DIP).
- Checkout service must not perform HTTP calls itself.
- No Salesforce API URLs inside checkout service.
- All DTOs must use TypeBox schemas.
- Each method must validate its inputs via schema before invoking the client.
This figure illustrates the end-to-end sequence flow between a Shopper, a Checkout Application, the Salesforce SLAS API, and Salesforce Shopper APIs during a typical checkout lifecycle. It shows how each system interacts during key actions: opening the checkout page, displaying products, modifying the basket, and submitting an order.
Figure 1
A FeathersJS application normally has the following folders under /src.
Each folder plays a specific role in organizing services, configuration, hooks, and application logic.
Note: This project omits the src/models folder because it does not use a database.
The main application bootstrap file.
Responsible for:
- Creating the Feathers app instance
- Registering middleware
- Registering services
- Registering channels
- Configuring transports (REST, WebSockets if enabled)
- Exporting the final initialized app This is the heart of the Feathers application.
The application entry point.
It typically:
- Imports the app from
app.ts - Starts the HTTP server
- Handles startup logging and error handling
Contains all the service definitions.
Each Feathers "service" is usually organized into its own folder, e.g.:
/src/services/
├─ shoppers/
│ ├─ shoppers.schema.ts
│ ├─ shoppers.class.ts
│ ├─ shoppers.ts
├─ checkout/
│ ├─ checkout.schema.ts
│ ├─ checkout.class.ts
│ ├─ checkout.tsEach service folder typically includes:
*.service.ts→ registers the service with Feathers*.model.ts→ contains the actual service class (business logic)*.schema.ts→ (optional but common) TypeBox schema definitions*.hooks.ts→ (optional) hooks applied before/after service methods
Contains reusable hooks that can be shared across services, such as:
- authorization hooks
- validation hooks
- data transformation hooks
- logging / timing hooks
Example:
/src/hooks/log-error.tsConfiguration files such as:
- default application config
- environment-specific config overrides
- CORS setup
- REST/WebSocket setup
A place for helpers and shared utilities such as:
- ID generators
- mapping / formatting functions
- external API helpers