Skip to content

Database Layer

The backend uses a layered database abstraction that decouples business logic from the underlying database technology.


Architecture

DatabaseInterface  (interfaces/database.interface.js)

        └── MongoDBDatabase  (database/mongodb/index.js)

                ├── UserDB          extends BaseDB
                ├── DeviceDB        extends BaseDB
                ├── UnitDB          extends BaseDB
                ├── SensorDataDB    extends BaseDB
                ├── DiagnosticDataDB extends BaseDB
                ├── RawSensorDataDB  extends BaseDB
                ├── NetworkDB        extends BaseDB
                ├── RecipientDB      extends BaseDB
                ├── NotificationDB   extends BaseDB
                ├── UnitAndDeviceDB  extends BaseDB
                └── TransactionDB

All business classes receive a db instance via constructor injection:

js
// Example in a controller
const db = getDatabase()
const business = new UserBusiness(db)

DatabaseInterface (Abstract Contract)

interfaces/database.interface.js defines the required getters. Attempting to use the base class directly throws 'Method not implemented':

js
get unit()         { throw new Error('Method not implemented') }
get device()       { throw new Error('Method not implemented') }
get user()         { throw new Error('Method not implemented') }
get transaction()  { throw new Error('Method not implemented') }
// ... etc.

MongoDBDatabase

database/mongodb/index.js extends DatabaseInterface and exposes composed DB operation instances as getters:

GetterDB ClassCollection(s)
.unitUnitDBunits
.deviceDeviceDBdevices
.unitAndDeviceUnitAndDeviceDBunit_and_device_logs, user_and_device_logs
.userUserDBusers, user_tokens
.sensorDataSensorDataDBsensor_data
.diagnosticDataDiagnosticDataDBdiagnostic_data
.rawSensorDataRawSensorDataDBraw_sensor_data
.networkNetworkDBnetwork_data
.notificationNotificationDBnotification_receivers
.recipientRecipientDBrecipients
.transactionTransactionDB(MongoDB sessions)

BaseDB (Generic CRUD)

database/mongodb/base.db.js provides lean generic operations that all DB classes inherit:

Methods

js
create(data)
// Mongoose Model.create(data) → returns lean plain object

find(query, options?)
// options: { skip, limit, sort, projection, populate }
// populate: string | string[] | { path, select }[]
// Returns lean array

findOne(query, options?)
// options: { populate, sort }
// Returns lean object or null

findById(id, options?)
// Returns lean object or null

update(id, data, options?)
// findByIdAndUpdate(id, data, { new: true })
// Returns updated lean object

updateOne(filter, update, options?)
// Model.updateOne(filter, update).lean()

updateMany(items)
// bulkWrite with individual updateOne operations
// items: [{ filter, update }]

All results are .lean() — plain JavaScript objects, not Mongoose documents. This is intentional for performance since business logic never needs Mongoose document methods.


UserDB (Extended)

UserDB adds methods beyond BaseDB:

MethodDescription
countDocuments(query)Count matching documents
save(user)Call .save() on a Mongoose document
createToken(userId, token)Insert into user_tokens
deleteToken(token)Remove from user_tokens by token value
aggregate(pipeline)Run an aggregation pipeline on users

Database Singleton

database/index.js implements factory + singleton:

js
let instance = null

export const initializeDatabase = (type = 'mongodb') => {
  instance = new MongoDBDatabase()
  return instance
}

export const getDatabase = () => {
  if (!instance) instance = new MongoDBDatabase()
  return instance
}

Transactions

TransactionDB wraps MongoDB sessions for atomic multi-document operations:

js
// Usage pattern in business classes
const session = await db.transaction.start()
try {
  await db.user.create({ ...userData }, { session })
  await db.recipient.create({ ...recipientData }, { session })
  await db.transaction.commit(session)
} catch (error) {
  await db.transaction.abort(session)
  throw error
}

Transactions are used in:

  • UserBusiness.signup() — creates user + default recipient atomically
  • UnitBusiness.updateUnits() — multi-document unit/device updates
  • RecipientBusiness.deleteRecipient() — removes from all units + soft-deletes
  • AdminDeviceBusiness.addDevicesToUser() — creates device logs + updates devices

Intecog Logistech IoT Monitoring Platform