برنامه نویسی

ساختن یک مخزن پایه مقیاس پذیر با Typecript & Mongoose

هنگامی که شما روی رشد Node.js با MongoDB کار می کنید ، هر مجموعه ای – تگ ، یادداشت ها ، کاربران و غیره – به همان منطق اصلی CRUD نیاز دارد.

با این حال ، سیم کشی فیلترها ، صفحه بندی ، طرح ریزی ، مرتب سازی ، جلسات و جمعیت برای هر مدل ، Boilerplate City است.

فریادهای غم انگیز

در این پست ، ما بررسی خواهیم کرد کاربردی رویکرد به ایجاد یک مجرد و عمومی مخزن پایه کارخانه در Typescript که:

  • تطبیق کردن تایپ قوی در مورد فیلترها ، پیش بینی ها و انواع
  • حمایت صفحه بندیبا تصفیهبا مرتب سازیبا جمعیتوت جلسات
  • است ، پسندیده-صفحه نمایش مکان نما ، جستجوی متن کامل ، یا تجمع در جاده
  • کد شما را نگه می دارد خشک و تیم شما خوشحال

مشکل: انفجار دیگ بخار

یک معمولی getAll روش برای TagDocument ممکن است اینگونه به نظر برسد:

async getAll(userId: ID, session?: ClientSession): Promise<TagDocument[]> {
  return unwrap(
    await mongo.fire(() =>
      tagModel.find({ user: userId }, null, { session })
    )
  );
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اما پس از نیاز به:

  • اضافه کردن صفحه بندی (.skip() / .limit())
  • اضافه کردن مرتب سازی (.sort())
  • حمایت پیش بینی (.find(filter, projection))
  • ضمیمه کردن جمعیت ()
  • نخ از طریق مشتری

… شما در پایان این منطق را در هر مخزن کپی خواهید کرد. از همه بدتر ، روش شما بالون را با پارامترهای اضافی و اضافه بار امضا می کند.

راه حل: createBaseRepository

به جای اینکه خود را تکرار کنید ، ما می سازیم کارخانه:

import type {
  ClientSession,
  FilterQuery,
  HydratedDocument,
  Model,
  PopulateOptions,
  ProjectionType,
  SortOrder,
} from "mongoose";
import { mongo } from "./mongo.config";
import { type CommandResult, unwrap } from "./global";

// 1) Pagination & sorting types
export interface PaginationOptions {
  page?: number;
  pageSize?: number;
}

export type SortBy<T> =
  | Partial<Record<Extract<keyof T, string>, SortOrder>>
  | [Extract<keyof T, string>, SortOrder][];

// 2) Fully-typed GetAll options
export interface GetAllOptions<T, Doc extends HydratedDocument<T>> {
  filter?: FilterQuery<Doc>;
  projection?: ProjectionType<Doc>;
  pagination?: PaginationOptions;
  sort?: SortBy<T>;
  session?: ClientSession;
  populate?: PopulateOptions | PopulateOptions[];
}

// 3) The factory function
export const createBaseRepository = <T, Doc extends HydratedDocument<T>>(
  model: Model<T, {}, Doc>
) => ({
  async getAll<F extends FilterQuery<Doc>>(
    opts: GetAllOptions<T, Doc>
  ): Promise<Doc[]> {
    const {
      filter = {} as F,
      pagination: { page = 1, pageSize = 10 } = {},
      projection,
      populate,
      sort,
      session,
    } = opts;

    let query = model.find(filter, projection ?? null, { session });

    // Pagination
    query = query.skip((page - 1) * pageSize).limit(pageSize);

    // Sorting
    if (sort) {
      if (Array.isArray(sort)) {
        query = query.sort(sort);
      } else {
        query = query.sort(sort as Record<string, SortOrder>);
      }
    }

    // Population
    if (populate) {
      query = query.populate(populate);
    }

    // Execute & unwrap
    const res = (await mongo.fire(() => query)) as CommandResult<Doc[]>;
    return unwrap(res);
  },
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


چگونه کار می کند

  1. انواع عمومی
  • T رابط طرحواره خام شماست (به عنوان مثال Note { title: "string; … })
  • Doc extends HydratedDocument نوع واقعی سند Mongoose است.
  1. GetAllOptions
  • filter: از هر فیلتر سبک Mongoose استفاده کنید.
  • projection: شامل/حذف زمینه ها.
  • pagination: شماره صفحه و اندازه صفحه.
  • sort: مرتب سازی به شدت توسط هر زمینه طرحواره.
  • session: موضوع الف ClientSession برای معاملات
  • populate: جمعیت استاندارد مونگوز.
  1. عمل
  • ما یک تک می سازیم model.find() پرس و جو ، سپس زنجیره ای .skip()با .limit()با .sort()وت .populate()بشر
  • ما await mongo.fire(() => query) (بسته بندی شما برای مدیریت اتصال) و unwrap() نتیجه

گسترش repo پایه در رابط های خود

اگر برای مخازن خود از رابط هایی استفاده می کنید ، می توانید به راحتی شکل repo پایه را گسترش دهید:

export interface INotesRepository
  extends ReturnType<typeof createBaseRepository<Note, NoteDocument>> {
  // add any note-specific methods here
  create(
    title: "string,"
    content: string,
    user: string,
    session?: ClientSession
  ): Promise<NoteDocument>;
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

سپس آن را پیاده سازی کنید:

export const notesRepository: INotesRepository = {
  ...createBaseRepository<Note, NoteDocument>(noteModel),

  async create(title, content, user, session) {
    const doc = new noteModel({ title, content, user });
    const res = (await mongo.fire(() => doc.save({ session }))) as CommandResult<NoteDocument>;
    return unwrap(res);
  },
};
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ضد آینده: جمع آوری و جستجوی متن

از آنجا که تمام پیکربندی پرس و جو شما در GetAlloptions زندگی می کند ، می توانید بعداً بدون تغییر امضای repo خود در مدل ها ، قابلیت های جدیدی را اضافه کنید. به عنوان مثال:

export interface GetAllOptions<T, Doc extends HydratedDocument<T>> {
  filter?: FilterQuery<Doc>;
  projection?: ProjectionType<Doc>;
  pagination?: PaginationOptions;
  sort?: SortBy<T>;
  session?: ClientSession;
  populate?: PopulateOptions | PopulateOptions[];
  // new feature flags:
  search?: string;
  useCursor?: boolean;
  aggPipeline?: PipelineStage[];
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در داخل اجرای GetAll خود ، می توانید در Opts.Search شاخه کنید ، یک فیلتر جستجوی متن بسازید ، یا در صورت ارائه Agpipeline یک جمع را اجرا کنید – همه با همان روش. لایه خدمات شما ثابت می ماند و توسعه دهندگان شما یک مکان برای یادگیری و گسترش دارند.

فواید

  • خشک و قابل نگهداری: همه منطق مشترک در یک مکان.
  • کاملاً تایپ شده: TypeScript فیلترهای نامعتبر ، پیش بینی ها یا انواع مختلف را به خود جلب می کند.
  • ضد آینده: با گسترش پشتیبانی از صفحه نمایش مکان نما ، جستجوی متن کامل یا جمع آوری اضافه کنید GetAllOptions و کارخانه یک بار
  • API سازگار: هر روش مخزن همان سبک امضا را دارد.

پایان

با استفاده از یک عمومی ، کاربردی createBaseRepository، شما یک پایه لاغر ، سازگار و آینده آماده برای همه مدل های Mongoose خود به دست می آورید. هیچ کپی بیشتر/چرت زدن. فقط یک بار بسازید ، در همه جا گسترش دهید و پایگاه کد خود را تمیز ، سریع و مقیاس پذیر نگه دارید.


اگر س questions ال دارید نظر خود را در زیر رها کنید! 💡

بیایید متصل شویم !!:

وابسته به لینکدین
لوب

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا