1

I have a hotel entity which has a set of images. I have some business rules which are basically simple crud operations right now. I have heard of repository pattern but working with it I feels like procedural mindset.

Let me give an example, I need to fetch all the images associated with a hotel.

'use strict';

const mysql = require('mysql');
const { queryPromise } = require('../utils');

const Image = function Image(opt_data) {
  const data = opt_data || {};

  if (!data['url']) {
    throw new Error('URL is required');
  }

  this.id = data['id'] || null;
  this.url = data['url'];
  this.type = data['type'] || Image.Type.HOTEL;
  this.resource_id = data['resource_id'] || null;

  this.updated_at = data['updated_at'] || new Date();
  this.created_at = data['created_at'] || new Date();
};
const ImageProto = Image.prototype;

ImageProto.save = function saveImage(context) {
  const query = `INSERT INTO ${Image.TABLE_} (type, url, resource_id) VALUES (?, ?, ?)`;
  return queryPromise(context, mysql.format(query, [
    this.type,
    this.url,
    this.resource_id
  ]));
};

ImageProto.toJSON = function() {
  return {
    id: this.id,
    url: this.url
  };
};

Image.Type = {
  HOTEL: 'hotel',
  ROOM: 'room',
  MERCHANT: 'merchant'
};

Image.TABLE_ = 'image';

const Images = function Images(opt_data) {
  const data = opt_data || {};

  this.hotelId = data['hotel_id'] || [];
  this.items = data['images'] || [];
};
const ImagesProto = Images.prototype;

ImagesProto.add = function addImage(imageData) {
  this.items.push(
      new Image(Object.assign({}, {resource_id: this.hotelId}, imageData)));
};

ImagesProto.remove = function removeImage(context, image) {
  const query = `DELETE FROM ${Image.TABLE_} WHERE id=?`;
  return queryPromise(context, mysql.format(query, [image.id]));
};

ImagesProto.save = function saveImage(context) {
  return Promise.all([
    this.items.map(img => img.save(context))
  ]);
};

ImagesProto.filter = function _addImage(context, args) {
  let query = args.map(function (arg) {
      var value = arg[2];
      if (typeof value === typeof '') {
        value = "'" + value + "'";
      }
      arg[2] = value;
      return arg.join('');
  }).join(' and ');

  query = `SELECT * FROM ${Image.TABLE_} WHERE ${query}`;
  return queryPromise(req, query)
      .then(function(results) {
        return new Image(results[0]);
      });
};

ImagesProto.fetch = function _addImage(context) {
  const query = `SELECT * FROM ${Image.TABLE_} WHERE resource_id=?`;
  return queryPromise(context, mysql.format(query, [this.hotelId]))
      .then(results => {
        return results.map(res => new Image(res));
      });
};

module.exports = { Image, Images };

HotelProto.images = function _images(context) {
  return new Images({ hotel_id: this.id });
};

Although it seems good but I am still not sure about this approach. I have heard my fellow developer that I am mixing command/query thing but I didn't get it properly.

Is there any better approach which sticks to the OOP?

Also, if in future I need to keep a record of all the activities then what changes I would need to do?

  • 3
    The purpose of a repository is to abstract away the details of retrieving data from the database, provide unit testing capability and the ability to swap out the database implementation. If you don't require those capabilities, then you may not need a repository layer. – Robert Harvey Jul 12 '17 at 17:48
  • In general, use tools and follow practices when you need to. Don't use them just because someone says its a good idea; that's not enough. You need to understand the motivations behind using a tool or following a practice first. Ask yourself "what problems does this tool/practice solve, do I need to solve those problems, and do the benefits outweigh the costs?" – Robert Harvey Jul 12 '17 at 17:50
  • @RobertHarvey my main motto is to have make my code more readable and object oriented. Yes, I need the capabilities you mentioned but was thinking if there is some better approach? – CodeYogi Jul 12 '17 at 17:55
  • Readability is a good goal to have. Object-orientation is a technique, not a goal. Is your only criteria for "better" readability and OO fashion? – Robert Harvey Jul 12 '17 at 17:57
  • 1
    Have a look at Javascript the Good Parts by Douglas Crockford. If you really want to work with OOP, you need something a bit stronger like Typescript. – Robert Harvey Jul 12 '17 at 21:00
3

You are right about this: looking at that code is painful. You are right to be looking for a better way to organize it.

Read this again and think seriously about what it means:

The purpose of a repository is to abstract away the details of retrieving data from the database, provide unit testing capability and the ability to swap out the database implementation. If you don't require those capabilities, then you may not need a repository layer.

Robert Harvey

This is 100% correct.

Here is your problem:

I have some business rules which are basically simple crud operations right now.

Code Yogi

As long your business rules ARE crud operations you don't have to worry about a repository layer because you don't HAVE business rules to separate from the database access code.

You have full blown Domain Driven Design here. This code speaks the language of your domain expert. Which by the way is SQL. :P

Your domain is "manipulate the database". There are tons of applications that work that way.

What you're lacking are business rules that have nothing to do with the database. If you don't have these there is nothing to separate.

So sit back and ask yourself: if I didn't have to worry about database details what would I worry about?

If you can't think of anything then don't complicate things because you're convinced layers are always good. They aren't. Until you see why you're using them, just don't.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.