Data access - Xantier/nerd-stack GitHub Wiki

Data access in our application is handled in our service and data access layers. The skeleton contains two simple examples of services and multiple implementations of different data access functions for different databases. In our application architecture we try to keep our service layer separate from database access so that we can change between databases fairly simply by implementing needed DAOs/Repositories for each database.

Our data access implementations live in app/data/[database]/persistence/ folder and services can be found from app/services/. At the moment we will fetch the repository implementation corresponding your chosen database to use in our services. We usually want to keep our services fairly lightweight so that we can exchange data access implementations easily. Since usually we expect some kind of normalized data model coming from our repository to the service it's important to keep in mind that initial normalization should probably be done in the repository and return an aggregate root to the service. In our simple implementations we don't have actual data access objects separated from repositories but if an extra layer of abstraction would be needed it could be a good idea to extract functions that handle only discussions with database to a different DAO.

A simple example of a service would be a method from our userService.js file:

  function get(req, res, next) {
    return thingRepository.getThingsById(req.db, req.user.id, (err, collection) => {
      if (err) {
        res.payload = {error: true, data: {message: err.message}};
      } else {
        res.payload = {things: collection};
      }
      return next();
    });
  }

In here we pass on relevant information to our repository and a call back that receives the collection we need. We populate response.payload with relevant data and call the next method in the chain. If the call for this method came from our apiRouter next() would be the respond method passing the payload back to browser using Express response. If the call came from server side next() would be our serverMediator returning the payload back to its caller.

Note that in here we are returning both the call to the repository and call to next. This is because our isomorphic architecture expects return value when it is populating the context for server rendered views. More info on this can be found from Control Flow article