samedi 10 juin 2023

What is the optimal structure for Gateways and Adapters in Clean Architecture?

I'm embarking on a project using Clean Architecture, TypeScript, and TDD. One of my primary concerns is structuring the gateways and adapters correctly, particularly database adapters.

There's some advice circulating around suggesting to avoid classes due to the overhead and resource usage. With this assumption in mind, I've explored two different methods for structuring gateways and adapters:

  • Approach 1: Define gateways as interfaces, with adapters being the implementations of these interfaces.
  • Approach 2: Define gateways as higher-order functions (functions that return functions), accepting adapters as arguments.

In this repository, I've made a simple implementation of these approaches.

The first approach appears to be safer, providing a clear contract for the adapters and is, arguably, more straightforward. On the other hand, the second approach provides more flexibility by enabling logic to be inserted into the gateways, albeit at the cost of reduced safety and increased complexity.

I'm interested in the community's thoughts and advice on these approaches. Which approach is recommended, and why? Can anyone provide some examples or feedback on the structure I've set up in my repository?

Below are some code snippets from the repository for reference:

Gateways:

interface OrderGateway1 {
  getAll: () => Promise<Order[] | undefined>
  getById: (orderId: string) => Promise<Order | undefined>
}

const orderGateway2 = (orderDbAdapter: any) => {
  return {
    getAll: (): Order[] => orderDbAdapter.getAll(),
    getById: (orderId: string): Order => orderDbAdapter.getById(orderId),
  }
}

In-memory Data Storage Adapters:

const orderInMemoryDbAdapter1: OrderGateway1 = (() => {
  const ordersDb: Order[] = [...inMemoryDb.orders]
  return {
    getAll: () => Promise.resolve(ordersDb),
    getById: (orderId: string) => Promise.resolve(ordersDb.find((order) => order.id === orderId)),
  }
})()


const orderInMemoryDbAdapter2 = () => {
  const ordersDb: Order[] = [...inMemoryDb.orders]
  return {
    getAll: () => ordersDb,
    getById: (orderId: string) => ordersDb.find((order) => order.id === orderId),
  }
}

JSON Server Data Storage Adapters:

const orderJsonServerDbAdapter1: OrderGateway1 = {
  getAll: async (): Promise<Order[] | undefined> => {
    const result = await api.get<Order[]>('/orders')
    return result
  },
  getById: async (id: string): Promise<Order | undefined> => {
    const result = await api.get<Order>(`/orders/${id}`)
    return result
  },
}

const orderJsonServerDbAdapter2 = () => {
  return {
    getAll: async (): Promise<Order[] | undefined> => {
      const result = await api.get<Order[]>('/orders')
      return result
    },
    getById: async (id: string): Promise<Order | undefined> => {
      const result = await api.get<Order>(`/orders/${id}`)
      return result
    },
  }
}

Aucun commentaire:

Enregistrer un commentaire