Skip to content

Server Service Modules

server-service modules contain all server-side business logic and orchestration. They are where use cases are implemented - what happens when a controller invokes an action. Services are the brain of the application.

On This Page


Purpose

  • Contain all server-side application and business logic
  • Orchestrate use cases by coordinating between models, server helpers, and other services
  • Receive validated DTOs from controllers and return structured envelopes ({ success, data } or { success, error })
  • Act as the brain of the application - they decide what happens when a controller invokes an action

In Domain-Driven Design terminology, this is the layer often called "Application Service" or "Use Case." We use the simpler name Service and the directory is src/server/service/.


Design Principles

PrincipleDetail
All business rules and orchestration hereNowhere else
Receives (instance, dto) from controllersExecution context first, validated data second; never raw request data
Interacts with infrastructure only through LibDBs, queues, cloud APIs are reached via injected helper modules
Stateless per requestNo hidden global state
Returns structured envelopes{ success, data } on success, { success, error } on failure
Transport-agnosticMust not know about HTTP, Lambda, Express, or any specific transport

Naming Convention

ElementConvention
Module directory name[entity-name] (singular)
Main file name[entity-name].service.js
Locationsrc/server/service/

For example, the user service lives at src/server/service/user.service.js.


Typical Files

FilePurpose
[entity].service.jsAll service functions for the entity. Each function represents a complete use case or workflow
index.js(Optional) public export surface if the service is split into multiple files

Typical Function Names

  • createUser(data) - create a new user
  • getUserById(id) - fetch user by ID
  • updateUserProfile(data) - update profile data
  • deleteUser(id) - soft-delete a user

Per-Function Responsibilities

StepDetail
Execute business rules and policiesThe decisions that make this entity what it is
Coordinate between multiple helpers and modelsDB writes, cache updates, downstream calls
Handle transactional logicMulti-step operations that must succeed or fail together
Translate helper-module errors into domain errorsMandatory - see Error Handling
Return a structured result{ success: true, data: ... } or { success: false, error: ... }

Module Initialization Pattern

Services receive Lib and Config via the loader. They use Lib to access helpers and other loaded modules; they use Config for environment-specific behavior.

javascript
// src/server/service/user.service.js

let Lib;
let Config;

const loader = function (shared_libs, config) {

  Lib = shared_libs;
  Config = config;

};

const UserService = {

  createUser: async function (instance, data) {

    // Business rule: check if email already exists
    const existing = await Lib.DB.queryRecords(instance, 'users', { email: data.email });
    if (existing.length > 0) {
      return { success: false, error: Lib.User.errors.EMAIL_ALREADY_EXISTS };
    }

    // Create record
    const record = await Lib.DB.addRecord(instance, 'users', data);

    // Return structured result
    return { success: true, data: record };

  }

};

export default function (shared_libs, config) {

  loader(shared_libs, config);
  return UserService;

};

Boundary Rules

Services may be used by

  • server-controller only

Services must NOT

  • Be called directly by interfaces (must go through controller)
  • Access raw request/response objects
  • Know about HTTP status codes or transport concerns
  • Import client-side code or client helpers

Services may

  • Receive the request instance as the first argument and pass it unchanged to helper modules
  • Use catalog-core (via Lib)
  • Use catalog-server (via Lib)
  • Use base-model and server-model (via Lib)
  • Call other services (via Lib)

Further Reading

Released under the MIT License.