Database Architecture
The eTeamups Platform uses MongoDB 7 as its primary data store, accessed through Mongoose 8.15.0 as the ODM layer. All models, schemas, and DTOs (TypeScript interfaces) are centralised in the services/store/ directory and shared across all services.
MongooseClient Singleton
Database connections are managed by the MongooseClient singleton class in libs/mongo/src/mongooseClient.ts. Key characteristics:
- Singleton pattern:
MongooseClient.getInstance()returns the same connection across all callers. - Auto-connection: The
BaseModelconstructor callsMongooseClient.getInstance()on every model instantiation, ensuring the connection is established before any query. - Connection string: Read from
MONGODB_URLorDATABASE_URLenvironment variable. - Timeout:
serverSelectionTimeoutMSis set to 5000 ms. - Event listeners: Logs
error,disconnected, andreconnectedevents.
class MongooseClient {
private static instance: mongoose.Connection;
static async getInstance(): Promise<mongoose.Connection> {
if (!MongooseClient.instance) {
await mongoose.connect(connectionString, {
serverSelectionTimeoutMS: 5000
});
MongooseClient.instance = mongoose.connection;
}
return MongooseClient.instance;
}
}
BaseModel Pattern
Every model class extends BaseModel<T>, which provides a standard set of CRUD operations and automatic field population.
Location: services/store/src/model/BaseModel.ts
Automatic Fields
On every create() call, BaseModel automatically sets:
| Field | Value |
|---|---|
id |
UUID v4 (via uuid package) |
createdAt |
Current timestamp |
updatedAt |
Current timestamp |
Provided Methods
| Method | Signature | Description |
|---|---|---|
create |
create(payload: Partial<T>) |
Insert a new document with auto-generated id and timestamps |
findOne |
findOne(filter: FilterQuery<T>) |
Find a single matching document |
findMany |
findMany(filter: FilterQuery<T>) |
Find all matching documents, sorted by createdAt descending |
delete |
delete(filter: FilterQuery<T>) |
Delete all matching documents (deleteMany) |
patch |
patch(filter: FilterQuery<T>, payload: UpdateQuery<T>) |
Update all matching documents (updateMany) |
Example Usage
export class AccountModel extends BaseModel<IAccount> {
constructor() {
super(AccountMongooseModel);
}
async find(payload: Partial<IAccount>): Promise<IAccount | null> {
return this.findOne({ emailAddress: payload.emailAddress });
}
}
Collections and Schemas
The platform uses 16 MongoDB collections. Each collection has a corresponding TypeScript interface (DTO) and Mongoose schema.
accounts
Stores user account records.
Interface (IAccount):
interface IAccount extends Document {
id: string;
firstName: string;
lastName: string;
emailAddress: string;
role: 'GHOST' | 'ADMIN' | 'EDITOR' | 'VIEWER';
status: 'ACTIVE' | 'PENDING' | 'BLOCKED' | 'REPORTED';
createdAt: Date;
updatedAt: Date;
}
Schema notes: id defaults to UUID v4. role defaults to 'VIEWER'. String fields firstName and lastName are trimmed.
otps
Stores one-time passwords for email-based authentication.
Interface (IOtp):
interface IOtp extends Document {
id: string;
emailAddress: string;
otp: string;
accountId: string;
status: 'PENDING' | 'VERIFIED' | 'EXPIRED' | 'USED';
codeChallenge: string;
createdAt: Date;
updatedAt: Date;
expiresAt: Date;
}
Schema notes: status is an enum. codeChallenge stores the PKCE challenge for verification. expiresAt is set to 5 minutes from creation. Previous OTPs for the same email are deleted before a new one is created.
tokens
Stores refresh tokens for JWT-based authentication.
Interface (IToken):
interface IToken extends Document {
id: string;
token: string;
phone_number_id: string;
expiresAt: Date;
createdAt: Date;
updatedAt: Date;
}
Schema notes: The phone_number_id field references the account ID (legacy naming from when the system used phone numbers).
organisations
Stores organisation configuration and settings.
Interface (IOrganisation):
interface IOrganisation extends Document {
id: string;
createdAt: Date;
updatedAt: Date;
algorithmVersion: 'v1' | 'v2';
genderInputOnRegister: boolean;
includeMatchUserInTeam: boolean;
isLocationPriority: boolean;
isPublic: boolean;
licenses: number;
logo?: string;
name: string;
productExpireTimestamp?: Date;
sampleAddTimestamp?: Date;
showMatches: boolean;
templatePrefix?: string;
urlName: string;
userChange: boolean;
workstyleWeight?: number;
}
Schema notes: name is unique with a max length of 100. id defaults to UUID v4. The schema uses Mongoose timestamps: true and has a pre-save hook to ensure id is set.
teammates
Stores individual teammate profiles within organisations.
Interface (ITeammates):
interface ITeammates extends Document {
_id: string;
title: string;
firstName: string;
lastName: string;
gender: 'MALE' | 'FEMALE';
emailAddress: string;
phoneNumber: string;
location_id: string;
profilePhoto: string;
organisation_id: string;
token: string;
accountActicationAt: Date;
lastLogin: Date;
createdAt: Date;
updatedAt: Date;
}
Schema notes: gender is an enum. location_id and organisation_id are string references.
teams
Stores team definitions within organisations.
Interface (ITeam):
interface ITeam extends Document {
_id: string;
organisation_id: string;
teamName: string;
teamDescription: string;
managers: string[];
createdAt: Date;
updatedAt: Date;
}
Schema notes: managers is an array of string IDs.
countries
Master data catalogue of countries.
Interface (ICountry):
interface ICountry {
id?: string;
createdAt: Date;
updatedAt: Date;
code: string;
deleted: boolean;
name?: string;
description?: string;
}
industries
Master data catalogue of industries.
Interface (IIndustry):
interface IIndustry {
id?: string;
createdAt: Date;
updatedAt: Date;
name: string;
orderBy: number;
description?: string;
}
functions
Master data catalogue of job functions/roles.
Interface (IFunction):
interface IFunction {
id?: string;
name: string;
orderBy: number;
createdAt: Date;
updatedAt: Date;
description?: string;
}
educationlevels
Master data catalogue of education levels.
Interface (IEducationLevel):
interface IEducationLevel {
id?: string;
createdAt: Date;
updatedAt: Date;
aliases?: string;
name: string;
orderIndex: number;
rank?: number;
description?: string;
}
studymajors
Master data catalogue of study majors.
Interface (IStudyMajor):
interface IStudyMajor {
id?: string;
name: string;
description?: string;
createdAt: Date;
updatedAt: Date;
}
locations
Master data catalogue of physical locations.
Interface (ILocation):
interface ILocation {
id?: string;
createdAt: Date;
updatedAt: Date;
linkedIdCode?: number;
name: string;
country_id: string;
address: string;
city: string;
state: string;
postalCode: string;
latitude?: number;
longitude?: number;
isActive?: boolean;
description?: string;
}
Schema notes: country_id is a string reference to the countries collection.
skills
Master data catalogue of skills.
Interface (ISkill):
interface ISkill {
id: string;
name: string;
description?: string;
orderBy: number;
createdAt: Date;
updatedAt: Date;
}
questionanswers
Stores answers linked to assessment questions.
Interface (IQuestionAnswer):
interface IQuestionAnswer {
id?: number;
createdAt: Date;
updatedAt: Date;
orderIndex: number;
text?: string;
value: number;
questionId: number;
}
userquestiongroupscores
Stores aggregated scores for user question groups.
Interface (IUserQuestionGroupScore):
interface IUserQuestionGroupScore extends Document {
id: number;
createdAT: Date;
updateAt: Date;
averageScore?: number;
publicDisplay: boolean;
questionGroup_id: number;
user_id: number;
}
apilogs
Stores API request/response logs for auditing and debugging.
Interface (IAPILog):
interface IAPILog extends Document {
id: string;
rawHeaders: string[];
url: string;
method: string;
remoteAddress: string;
remoteFamily: string;
httpVersion: string;
body: any;
params: any;
query: any;
startTime: Date;
endTime: Date;
processingTime: number;
createdAt: Date;
updatedAt: Date;
}
Schema notes: id defaults to UUID v4. body, params, and query are stored as generic objects.
Entity Relationship Diagram
File Organisation
services/store/src/
dto/
IAccount.ts
IOtp.ts
IToken.ts
IOrgainisation.ts
ITeam.ts
ITeammates.ts
ICountry.ts
IIndustry.ts
IFunction.ts
IEducationLevel.ts
IStudyMajor.ts
ILocation.ts
ISkill.ts
IQuestionAnswer.ts
IUserQuestionGroupScore.ts
IAPILog.ts
schema/
AccountSchema.ts
OtpSchema.ts
TokenSchema.ts
OrganisationSchema.ts
TeamSchema.ts
TeammatesSchema.ts
CountrySchema.ts
IndustrySchema.ts
FunctionSchema.ts
EducationLevelSchema.ts
StudyMajorSchema.ts
LocationSchema.ts
SkillSchema.ts
QuestionAnswerSchema.ts
UserQuestionGroupScoreSchema.ts
APILogSchema.ts
model/
BaseModel.ts
AccountModel.ts
OtpModel.ts
TokenModel.ts
OrganisationModel.ts
TeamModel.ts
TeammatesModel.ts
CountryModel.ts
IndustryModel.ts
FunctionModel.ts
EducationLevelModel.ts
StudyMajorModel.ts
LocationModel.ts
SkillModel.ts
QuestionAnswerModel.ts
UserQuestionGroupScoreModel.ts
APILogModel.ts
Indexing Strategy
The current schema definitions rely primarily on MongoDB’s default _id index plus a few explicit constraints:
| Collection | Index / Constraint |
|---|---|
| accounts | id (UUID, set on create) |
| organisations | name (unique, maxlength 100), id (UUID) |
| otps | Queried by accountId + otp + status + expiresAt |
| tokens | Queried by token + expiresAt |
For production workloads, consider adding compound indexes on the frequently queried filter combinations listed above to improve query performance.