blazejkustra / dynamode

Dynamode is a modeling tool for Amazon's DynamoDB
MIT License
61 stars 2 forks source link

Multi table design support #8

Closed gurkerl83 closed 10 months ago

gurkerl83 commented 10 months ago

Hi, thx for the library, just getting started and the feature description mentions "DynamoDB single-table design support". Quick question, does the library also support multi-table design?

Thx!

blazejkustra commented 10 months ago

Hi, yes it does! Simply create more than one table and it should work out of the box 👍

Example from one of my projects:

UniqueTable:

import attribute from 'dynamode/decorators';
import Entity from 'dynamode/entity';
import TableManager from 'dynamode/table';

import { UserTablePrimaryKey } from '../user/UserTable';

import '../../utils/aws';

export type UniqueTableProps = {
  value: string;
  type: 'email' | 'username';
  source: UserTablePrimaryKey;

  createdAt?: Date;
  updatedAt?: Date;
};

export const UNIQUE_TABLE_NAME = process.env.UNIQUE_TABLE_NAME || 'unique-development';

export default class UniqueTable extends Entity {
  @attribute.partitionKey.string()
  value: string;

  @attribute.string()
  type: string;

  @attribute.object()
  source: UserTablePrimaryKey;

  @attribute.date.string()
  createdAt: Date;

  @attribute.date.string()
  updatedAt: Date;

  constructor(props: UniqueTableProps) {
    super(props);

    this.value = props.value;
    this.type = props.type;
    this.source = props.source;
    this.createdAt = props.createdAt || new Date();
    this.updatedAt = props.updatedAt || new Date();
  }
}

export const UniqueTableManager = new TableManager(UniqueTable, {
  tableName: UNIQUE_TABLE_NAME,
  partitionKey: 'value',
  createdAt: 'createdAt',
  updatedAt: 'updatedAt',
});

export const UniqueEntityManager = UniqueTableManager.entityManager();

UserTable:

import attribute from 'dynamode/decorators';
import Entity from 'dynamode/entity';
import TableManager from 'dynamode/table';

import '../../utils/aws';

export type UserTablePrimaryKey = {
  pk: string;
  sk: string;
};

export type UserTableProps = UserTablePrimaryKey & {
  createdAt?: Date;
  updatedAt?: Date;

  gsi_pk_2?: string;
  gsi_sk_2?: string;

  gsi_pk_3?: string;
  gsi_sk_3?: string;
};

export const USER_TABLE_NAME = process.env.USER_TABLE_NAME || 'user-development';
export const DYNAMODE_INDEX = 'dynamode-index';
export const GSI_2_INDEX = 'GSI_2_INDEX';
export const GSI_3_INDEX = 'GSI_3_INDEX';

export default class UserTable extends Entity {
  @attribute.partitionKey.string()
  pk: string;

  @attribute.sortKey.string()
  sk: string;

  @attribute.gsi.partitionKey.string({ indexName: DYNAMODE_INDEX })
  dynamodeEntity!: string;

  @attribute.gsi.sortKey.string({ indexName: DYNAMODE_INDEX })
  gsi_sk_1: string;

  @attribute.gsi.partitionKey.string({ indexName: GSI_2_INDEX })
  gsi_pk_2?: string;

  @attribute.gsi.sortKey.string({ indexName: GSI_2_INDEX })
  gsi_sk_2?: string;

  @attribute.gsi.partitionKey.string({ indexName: GSI_3_INDEX })
  gsi_pk_3?: string;

  @attribute.gsi.sortKey.string({ indexName: GSI_3_INDEX })
  gsi_sk_3?: string;

  @attribute.date.string()
  createdAt: Date;

  @attribute.date.string()
  updatedAt: Date;

  constructor(props: UserTableProps) {
    super(props);

    this.pk = props.pk;
    this.sk = props.sk;
    this.createdAt = props.createdAt || new Date();
    this.updatedAt = props.updatedAt || new Date();
    this.gsi_sk_1 = this.createdAt.toISOString();
    this.gsi_pk_2 = props.gsi_pk_2;
    this.gsi_sk_2 = props.gsi_sk_2;
    this.gsi_pk_3 = props.gsi_pk_3;
    this.gsi_sk_3 = props.gsi_sk_3;
  }
}

export const UserTableManager = new TableManager(UserTable, {
  tableName: USER_TABLE_NAME,
  partitionKey: 'pk',
  sortKey: 'sk',
  indexes: {
    [DYNAMODE_INDEX]: {
      partitionKey: 'dynamodeEntity',
      sortKey: 'gsi_sk_1',
    },
    [GSI_2_INDEX]: {
      partitionKey: 'gsi_pk_2',
      sortKey: 'gsi_sk_2',
    },
    [GSI_3_INDEX]: {
      partitionKey: 'gsi_pk_3',
      sortKey: 'gsi_sk_3',
    },
  },
  createdAt: 'createdAt',
  updatedAt: 'updatedAt',
});

You can extend the table to create 'entities', for example Item:

import attribute from 'dynamode/decorators';

import UserTable, { UserTableManager, UserTablePrimaryKey, UserTableProps } from './UserTable';

type ItemProps = UserTableProps & {
  name: string;
  quantity: number;
  category: string;
  isChecked: boolean;
  isUnavailable: boolean;
};

export default class Item extends UserTable {
  @attribute.sortKey.string({ prefix: Item.name })
  sk!: string;

  @attribute.string()
  listId: string;

  @attribute.string()
  itemId: string;

  @attribute.string()
  name: string;

  @attribute.number()
  quantity: number;

  @attribute.string()
  category: string;

  @attribute.boolean()
  isChecked: boolean;

  @attribute.boolean()
  isUnavailable: boolean;

  constructor(props: ItemProps) {
    super(props);

    this.listId = props.pk;
    this.itemId = props.sk;
    this.name = props.name;
    this.quantity = props.quantity;
    this.category = props.category;
    this.isChecked = props.isChecked;
    this.isUnavailable = props.isUnavailable;
  }

  static getPrimaryKey(listId: string, itemId: string): UserTablePrimaryKey {
    return {
      pk: listId,
      sk: itemId,
    };
  }
}

export const ItemManager = UserTableManager.entityManager(Item);
blazejkustra commented 10 months ago

If you have any issues/feedback about the library I'm open to help, good luck!

gurkerl83 commented 10 months ago

Thx for the quick response, really appreciate it! I will try.

Thx!