Client interfaces define the contract between our application and external
services. They play a crucial role in our architecture, allowing us to
implement the dependency inversion principle and greatly simplify testing.
Client interfaces serve two critical purposes in our architecture:
Dependency Inversion
They allow our application to depend on abstractions rather than concrete implementations, making it possible to swap one implementation for another without changing the consuming code.
Testability
They make it easy to create mock implementations for testing, eliminating the need to interact with real external services during tests.
/** * Interface for interacting with the Hubrise API. */export interface IHubriseClient { /** * Creates a new order in Hubrise. * * @param {Order} order - The order to create * @returns {Promise<HubriseOrderResponse>} The created Hubrise order * @throws {HubriseIntegrationError} When the API call fails */ createOrder(order: Order): Promise<HubriseOrderResponse>; /** * Retrieves an order from Hubrise. * * @param {string} orderId - The ID of the order to retrieve * @returns {Promise<HubriseOrderResponse>} The Hubrise order * @throws {HubriseIntegrationError} When the API call fails */ getOrder(orderId: string): Promise<HubriseOrderResponse>; /** * Updates the status of an order in Hubrise. * * @param {string} orderId - The ID of the order to update * @param {OrderStatus} status - The new status * @returns {Promise<HubriseOrderResponse>} The updated Hubrise order * @throws {HubriseIntegrationError} When the API call fails */ updateOrderStatus( orderId: string, status: OrderStatus ): Promise<HubriseOrderResponse>; /** * Creates a new customer in Hubrise. * * @param {Customer} customer - The customer to create * @returns {Promise<HubriseCustomerResponse>} The created Hubrise customer * @throws {HubriseIntegrationError} When the API call fails */ createCustomer(customer: Customer): Promise<HubriseCustomerResponse>; /** * Retrieves the catalog from Hubrise. * * @param {string} locationId - The ID of the location * @returns {Promise<HubriseCatalogResponse>} The Hubrise catalog * @throws {HubriseIntegrationError} When the API call fails */ getCatalog(locationId: string): Promise<HubriseCatalogResponse>;}
/** * Interface for interacting with the Stripe API. */export interface IStripeClient { /** * Creates a checkout session for an order. * * @param {Order} order - The order to create a checkout session for * @returns {Promise<StripeCheckoutSessionResponse>} The created checkout session * @throws {StripeIntegrationError} When the API call fails */ createCheckoutSession(order: Order): Promise<StripeCheckoutSessionResponse>; /** * Retrieves a checkout session. * * @param {string} sessionId - The ID of the session to retrieve * @returns {Promise<StripeCheckoutSessionResponse>} The checkout session * @throws {StripeIntegrationError} When the API call fails */ getCheckoutSession(sessionId: string): Promise<StripeCheckoutSessionResponse>; /** * Creates a payment intent. * * @param {number} amount - The amount to charge * @param {string} currency - The currency to use * @param {string} customerId - The ID of the customer * @returns {Promise<StripePaymentIntentResponse>} The created payment intent * @throws {StripeIntegrationError} When the API call fails */ createPaymentIntent( amount: number, currency: string, customerId: string ): Promise<StripePaymentIntentResponse>; /** * Verifies a webhook signature. * * @param {string} payload - The webhook payload * @param {string} signature - The webhook signature * @returns {Promise<boolean>} True if the signature is valid * @throws {StripeIntegrationError} When verification fails */ verifyWebhookSignature(payload: string, signature: string): Promise<boolean>;}
/** * Interface for interacting with the Mapbox API. */export interface IMapboxClient { /** * Geocodes an address to get coordinates. * * @param {string} address - The address to geocode * @returns {Promise<MapboxGeocodingResponse>} The geocoding result * @throws {MapboxIntegrationError} When the API call fails */ geocodeAddress(address: string): Promise<MapboxGeocodingResponse>; /** * Gets directions between two points. * * @param {Location} origin - The starting location * @param {Location} destination - The destination location * @returns {Promise<MapboxDirectionsResponse>} The directions * @throws {MapboxIntegrationError} When the API call fails */ getDirections( origin: Location, destination: Location ): Promise<MapboxDirectionsResponse>; /** * Calculates the estimated time of arrival. * * @param {Location} origin - The starting location * @param {Location} destination - The destination location * @returns {Promise<number>} The estimated time in minutes * @throws {MapboxIntegrationError} When the API call fails */ getEstimatedTimeOfArrival( origin: Location, destination: Location ): Promise<number>;}
One of the key benefits of using interfaces is the ability to easily swap
implementations:
Copy
// Switching from Mapbox to Google Maps@Module({ providers: [ CreateOrderUseCase, { provide: IMapboxClient, useClass: GoogleMapsClientAdapter, // Implements IMapboxClient with Google Maps API }, ],})export class OrderModule {}