Contact Me

Ports and Adapters (Hexagonal Architecture)

Decouple an application'as business logic from its external interactions through the use of ports and adapters.

 The basic idea behind ports and adapters is that the application's business logic is contained within a central 'hexagon' component, which communicates with the outside world through a set of 'ports.'

hexagonalArchImg

 These ports define the interface through which other components can interact with the hexagon, but they do not contain any implementation details. Instead, the implementation details are contained within 'adapters,' which are responsible for translating between the hexagon's ports and the specific technologies or protocols that the system uses.

 For example, imagine that we are building a web application that allows users to manage their contacts. The hexagon of the application would contain the business logic for managing contacts, such as adding new contacts, deleting contacts, and searching for contacts. The hexagon would communicate with the outside world through a set of ports, such as a 'contacts port' and a 'search port.'

 The ports would define the interface that other components must use to interact with the hexagon, but they would not contain any implementation details. Instead, the implementation details would be contained within adapters. For example, there might be a 'web adapter' that is responsible for handling HTTP requests and responses, and a 'database adapter' that is responsible for interacting with the database where the contacts are stored.

 Here is a brief example of how we can use javascript to implement the Ports and Adapters pattern:

class ContactsPort {

async addContact(contact) {}

async deleteContact(contactId) {}

async searchContacts(query) {}

}

class WebAdapter {

constructor(contactsPort) {

  this.contactsPort = contactsPort;

}

async handleAddContactRequest(req, res) {

  const contact = req.body;

  await this.contactsPort.addContact(contact);

  res.send('Contact added successfully');

}

async handleDeleteContactRequest(req, res) {

  const contactId = req.params.id;

  await this.contactsPort.deleteContact(contactId);

  res.send('Contact deleted successfully');

}

async handleSearchContactsRequest(req, res) {

  const query = req.query.q;

  const contacts = await this.contactsPort.searchContacts(query);

  res.json(contacts);

}

}

class DatabaseAdapter {

constructor(contactsPort) {

  this.contactsPort = contactsPort;

  this.db = new Database();

}

async addContact(contact) {

  await this.db.insert(contact);

}

async deleteContact(contactId) {

  await this.db.delete(contactId);

}

async searchContacts(query) {

  return await this.db.search(query);

}

}

 In this example, the ContactsPort defines the interface that other components must use to interact with the hexagon. The WebAdapter and the DatabaseAdapter are responsible for handling HTTP requests and responses and interacting with the database respectively. By using the adapter pattern, we can ensure that the hexagon is completely decoupled from the specific technologies or protocols that the system uses, making it easy to test and maintain.