CodebaseExamples
Custom Adapter
These examples show how to create custom implementations of domain ports.
Custom Adapter Examples
These examples show how to create custom implementations of domain ports.
Examples
1. Fake Geocoding Service (fake-geocoding-service.ts)
Create a simple in-memory geocoding service for testing or development.
bun run examples/custom-adapter/fake-geocoding-service.ts
What it demonstrates:
- Implementing a domain port (GeocodingServicePort)
- Creating test/development adapters
- Using custom adapters in place of real services
2. Redis Cache (redis-cache-example.ts)
Replace in-memory cache with Redis (conceptual example).
What it demonstrates:
- Implementing CacheServicePort with external service
- Swapping adapters without changing domain code
- Adapter pattern benefits
Key Concept: Port/Adapter Pattern
The domain defines what it needs (port/interface):
// Port defined by domain
export interface GeocodingServicePort {
geocodeAddress(address: AddressInput): Promise<GeocodingResult>;
geocodeAddresses(
addresses: readonly AddressInput[],
): Promise<GeocodingResult[]>;
}
Adapters provide how to get it (implementation):
// Adapter 1: Real NJ API
const njApi = createNjGeocodingClient(httpClient);
// Adapter 2: Fake for testing
const fakeApi = createFakeGeocodingService();
// Adapter 3: Google Maps
const googleMaps = createGoogleMapsClient(apiKey);
// All implement the same interface - domain doesn't care which one
const lookupAddress = createLookupAddressUseCase(njApi, cache);
Creating Your Own Adapter
- Import the port interface:
import type { GeocodingServicePort } from "../../src/domain/ports/geocoding-service";
- Create factory function that returns the port type:
export const createMyGeocodingClient = (
...dependencies
): GeocodingServicePort => ({
async geocodeAddress(address: AddressInput): Promise<GeocodingResult> {
// Your implementation
},
async geocodeAddresses(
addresses: readonly AddressInput[],
): Promise<GeocodingResult[]> {
// Your implementation
},
});
- Use your adapter:
const myService = createMyGeocodingClient();
const lookupAddress = createLookupAddressUseCase(myService, cache);
Available Ports to Implement
- GeocodingServicePort - Address → Municipality resolution
- SuggestionsServicePort - Address autocomplete
- CacheServicePort - Result caching
- HttpClientPort - HTTP requests
- LoggerPort - Structured logging
See src/domain/ports/ for interface definitions.