In my Laravel REST API project I mainly consume 3rd party APIs. For that, I have a 'Services' folder grouped by APIs (Accommodation, Planning, Discount etc) to send correct parameters to APIs and get the raw data.
I feel that I need to use adapter pattern here, because this 3rd party outputs need to be formatted.
Let me try to give an example.
I have an EventDao interface (Data access object) and one or multiple concrete EventDao classes like EventbriteDao.
I also have an adapter for each concrete DAO. e.g: EvenbriteAdapter
I will have some business logic somewhere so I need to have an Event entity class. To pass the adapter's data to the entity, I need an EventDTO class (data transfer object)
Finally I can call the eventDao (via interface thanks to Laravel's service providers)
I pass its raw output to the adapter, then adapter's output to the entity. Then I can call one of methods of the entity class. (After that I need to convert it to a proper json data, but it is the easy part.)
I don't get where to put other CRUD logic like updateEvent, deleteEvent, getAll etc. Should I call them directly in controller or create repositories (the repository pattern)? I am confused about it.
Would it be a good approach or over-engineering? Because I have 5-6 classes/interfaces other than controller.
Another problem is calling EventbriteAdapter directly in the controller. Should I have an interface for it? In this case I will need to bind both service and adapter interfaces to its implementations in AppServiceProvider.
My another concern is having collections for entity chunks. If I call getAll() method I can loop through the data and create an array of objects, but I am not comfortable with it. Laravel's collections would be useful, but I want to separate application layer from domain. What would be a good solution for it?
Here example codes for some of the files I mentioned are:
interface EventAdapter
{
public function getId();
public function getName();
public function getStartDate();
public function getLocationName();
}
class EventbriteAdapter implements EventInterface
{
private $raw;
public function __construct(array $raw)
{
$this->raw = $raw;
}
public function getName()
{
return $this->raw['name'];
}
public function getStartDate()
{
return $this->raw['start'];
}
public function getLocation()
{
return $this->raw['venue']['name'].' '.$this->raw['venue']['address'];
}
}
// Fetch event from Eventbrite (or other third-party)
$result = fetchEventFromEventbrite();
// Wrap event in adapter
$adapter = new EventbriteAdapter($result);
I don't get where to put other CRUD logic like updateEvent, deleteEvent, getAll etc. Should I call them directly in controller or create repositories (the repository pattern)? I am confused about it.
You can use the Gateway pattern to encapsulate all logic of interaction with 3rd party API. The gateway can contain methods to get data and save data. For example:
interface EventGatewayInterface
{
/**
* Get an event by id
*
* @param mixed $id unique identifier of an event
* @return Event the event
*/
public function getById($id);
/**
* Get all events
*
* @return Event[] list of events
*/
public function getAll();
/**
* Add an event
*
* @param Event $event the event which will be added
*/
public function add(Event $event);
/**
* Update an event
*
* @param Event $event the event which will be updated
*/
public function update(Event $event);
/**
* Delete an event
*
* @param Event $event the event which will be removed
*/
public function delete();
}
Would it be a good approach or over-engineering? Because I have 5-6 classes/interfaces other than controller.
In fact, you use the Adapter pattern to translate one representation (raw representation) to another (object representation). But the Adapter pattern is used to translate one program interface into a compatible program interface. To convert representations used the Mapper pattern. You can implement simple mapper for example:
class RawEventMapper
{
/**
* Map raw data to an event object
*
* @param array $data raw data
* @return Event the event object
*/
public function map(array $data)
{
$event = new Event();
$event->name = $data['name'];
$event->startDate = $data['start'];
$event->location = $data['venue']['name'].' '.$data['venue']['address'];
return $event;
}
}
and use it in Gateway to convert raw data to an object.
class ConcreteEventGateway implement EventGatewayInterface
{
/** @var RawEventMapper data mapper */
protected $mapper;
/**
* Constructor
*
* @param RawEventMapper $mapper data mapper
*/
public function __construct(RawEventMapper $mapper)
{
$this->mapper = $mapper;
}
/** @inheritdoc */
public function getById($id)
{
// get raw data from 3rd party API
return $this->mapper->map($raw);
}
}
Laravel's collections would be useful, but I want to separate application layer from domain. What would be a good solution for it?
If you aim to create a domain layer independent on the framework you shouldn't use Laravel collection.