Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * ---------------------------
- * Domain Layer
- * ---------------------------
- */
- class Order {
- /**
- *
- * @param {Object} prop
- * @param {string} prop.id
- * @param {string} prop.userId
- * @param {string} prop.storeId
- * @param {string} prop.orderNo
- * @param {Order.STATUS_ENUM} prop.status
- * @param {[OrderItem]} prop.items
- * @param {string} prop.createdAt
- * @param {string} prop.updatedAt
- */
- constructor({
- id,
- userId,
- storeId,
- orderNo,
- status,
- items,
- createdAt,
- updatedAt,
- }) {
- this.id = id;
- this.userId = userId;
- this.storeId = storeId;
- this.orderNo = orderNo;
- this.status = status;
- this.items = items;
- this.createdAt = createdAt;
- this.updatedAt = updatedAt;
- this._getNowIso = () => new Date().toISOString();
- }
- setGetNowIso(_getNowIso) {
- this._getNowIso = _getNowIso;
- }
- cancel() {
- if (
- this.status === Order.STATUS_ENUM.CANCELED ||
- this.status === Order.STATUS_ENUM.CLOSED
- ) {
- throw new Error();
- }
- this.status = Order.STATUS_ENUM.CANCELED;
- }
- close() {
- if (
- this.status === Order.STATUS_ENUM.CANCELED ||
- this.status === Order.STATUS_ENUM.CLOSED
- ) {
- throw new Error();
- }
- this.status = Order.STATUS_ENUM.CLOSED;
- }
- static create({ id, userId, storeId, orderNo, items }) {
- return new Order({
- id,
- userId,
- storeId,
- orderNo,
- status: Order.STATUS_ENUM.CREATED, // business rule
- items,
- createdAt: new Date().toISOString(),
- updatedAt: new Date().toISOString(),
- });
- }
- }
- /** @enum {string} - status of the order */
- Order.STATUS_ENUM = {
- CREATED: 'CREATED',
- CLOSED: 'CLOSED',
- CANCELED: 'CANCELED',
- };
- class OrderItem {
- /**
- * @param {Object} prop
- * @param {string} prop.orderId
- * @param {string} prop.variantId
- * @param {OrderItem.TYPE_ENUM} prop.type
- * @param {string} prop.name
- * @param {number} prop.price
- * @param {number} prop.count
- */
- constructor({ orderId, variantId, type, name, price, count }) {
- if (type === OrderItem.TYPE_ENUM.PRODUCT && price < 0) {
- throw new Error();
- } else if (type === OrderItem.TYPE_ENUM.GIFT && price !== 0) {
- throw new Error();
- }
- this.orderId = orderId;
- this.type = type;
- this.variantId = variantId;
- this.name = name;
- this.price = price;
- this.count = count;
- }
- /**
- * @returns {number}
- */
- // @ts-ignore
- get total() {
- const total = this.total * this.count;
- return this.type === OrderItem.TYPE_ENUM.DISCOUNT ? -1 * total : total;
- }
- changePrice(price) {
- return new OrderItem({ ...this, price });
- }
- /**
- * @param {Object} prop
- * @param {string} prop.orderId
- * @param {string|null} prop.variantId
- * @param {string} prop.name
- * @param {number} prop.price
- * @param {number} prop.count
- */
- static createProductTypeItem({ orderId, variantId, name, price, count }) {
- return new OrderItem({
- orderId,
- type: OrderItem.TYPE_ENUM.PRODUCT,
- name,
- price,
- count,
- variantId,
- });
- }
- /**
- * @param {Object} prop
- * @param {string} prop.orderId
- * @param {string} prop.variantId
- * @param {string} prop.name
- * @param {number} prop.count
- */
- static createGiftTypeItem({ orderId, variantId, name, count }) {
- return new OrderItem({
- orderId,
- type: OrderItem.TYPE_ENUM.GIFT,
- name,
- price: 0,
- count,
- variantId,
- });
- }
- /**
- * @param {Object} prop
- * @param {string} prop.orderId
- * @param {string} prop.name
- * @param {number} prop.price
- */
- static createDiscountTypeItem({ orderId, name, price }) {
- return new OrderItem({
- orderId,
- type: OrderItem.TYPE_ENUM.DISCOUNT,
- name,
- price,
- count: 1,
- variantId: null,
- });
- }
- /**
- * @param {Object} prop
- * @param {string} prop.orderId
- * @param {string} prop.name
- * @param {number} prop.price
- */
- static createShipmentFeeTypeItem({ orderId, name, price = 0 }) {
- return new OrderItem({
- orderId,
- type: OrderItem.TYPE_ENUM.SHIPMENT_FEE,
- name,
- price,
- count: 1,
- variantId: null,
- });
- }
- /**
- * @param {Object} prop
- * @param {string} prop.orderId
- * @param {string} prop.name
- * @param {number} prop.price
- */
- static createPaymentFeeTypeItem({ orderId, name, price = 0 }) {
- return new OrderItem({
- orderId,
- type: OrderItem.TYPE_ENUM.PAYMENT_FEE,
- name,
- price,
- count: 1,
- variantId: null,
- });
- }
- }
- /** @enum {string} - type of the order item */
- OrderItem.TYPE_ENUM = {
- PRODUCT: 'PRODUCT',
- DISCOUNT: 'DISCOUNT',
- GIFT: 'GIFT',
- SHIPMENT_FEE: 'SHIPMENT_FEE',
- PAYMENT_FEE: 'PAYMENT_FEE',
- };
- module.exports = {
- Order,
- OrderItem,
- };
- /**
- * ---------------------------
- * Domain Layer Testing
- * ---------------------------
- */
- const test = require('ava');
- const create_testcases = [];
- const create_testMacro = (t, input) => {
- Order.create(input);
- t.pass();
- };
- create_testcases.forEach(tc =>
- // @ts-ignore
- test(tc.description, create_testMacro, tc.input)
- );
- const cancel_testcases = [];
- const cancel_testMacro = (t, input) => {
- const o = Order.create(input);
- o.cancel();
- t.is(o.status, Order.STATUS_ENUM.CANCELED);
- };
- cancel_testcases.forEach(tc =>
- // @ts-ignore
- test(tc.description, cancel_testMacro, tc.input)
- );
- /**
- * ---------------------------
- * Repository Layer
- * * a repository class
- * * a toXXX helper function
- * * a toDb helper function
- * ---------------------------
- */
- class OrderRepository {
- /** * @param {Object} db */
- constructor(db) {
- this.db = db;
- }
- /**
- * @param {string} id
- * @returns {Promise<Order>}
- */
- async getOrderById(id) {
- const dbData = await this.db.query('SELECT data FROM order WHERE id = $1', [
- id,
- ]);
- return toOrder(dbData);
- }
- /**
- * @param {Order} order
- * @returns {Promise<Order>}
- */
- async insertOrder(order) {
- const dbData = await this.db.query(
- 'INSERT INTO order (data) VALUES ($1) RETURNING data',
- [toDb(order)]
- );
- return toOrder(dbData);
- }
- /**
- * @param {string} id
- * @param {Order} order
- * @returns {Promise<Order>}
- */
- async updateOrder(id, order) {
- const dbData = await this.db.query(
- 'UPDATE order set data = ($1) WHERE id = $2 RETURNING data',
- [toDb(order), id]
- );
- return toOrder(dbData);
- }
- }
- /**
- * @param {Object} data
- * @returns {Order}
- */
- function toOrder(data) {
- return new Order({
- id: data.id,
- userId: data.userId,
- storeId: data.storeId,
- orderNo: data.orderNo,
- status: data.status,
- items: data.items.map(item => new OrderItem(item)),
- createdAt: data.createdAt,
- updatedAt: data.updatedAt,
- });
- }
- /**
- * @param {Order} order
- * @returns {Object}
- */
- function toDb(order) {
- return { orderNo: order.orderNo, ...order };
- }
- /**
- * ---------------------------
- * Application Layer
- * ---------------------------
- */
- const uuidV4 = require('uuid/v4');
- /**
- * Use case1: createOrder
- * @param {Object} param
- * @param {Object} param.input
- * @param {OrderRepository} param.orderRepo
- * @returns {Promise<Order>}
- */
- async function createOrder({ input, orderRepo }) {
- const {
- userId,
- storeId,
- orderNo,
- products,
- payment,
- shipment,
- discount,
- } = input;
- const orderId = uuidV4();
- const order = Order.create({
- userId,
- storeId,
- orderNo,
- id: orderId,
- items: [
- OrderItem.createPaymentFeeTypeItem({ orderId, ...payment }),
- OrderItem.createShipmentFeeTypeItem({ orderId, ...shipment }),
- OrderItem.createDiscountTypeItem({ orderId, ...discount }),
- ...products.map(p => OrderItem.createProductTypeItem({ orderId, ...p })),
- ],
- });
- await orderRepo.insertOrder(order);
- return order;
- }
- /**
- * Use case 2: cancel order
- * @param {Object} param
- * @param {string} param.id
- * @param {OrderRepository} param.orderRepo
- * @returns {Promise<Order>}
- */
- async function cancelOrder({ id, orderRepo }) {
- const order = await orderRepo.getOrderById(id);
- order.cancel();
- await orderRepo.updateOrder(id, order);
- return order;
- }
- /**
- * ---------------------------
- * Entry point
- * ---------------------------
- */
- async function entryPoint() {
- const input = {
- userId: '11111111-1111-1111-1111-111111111111',
- storeId: '22222222-2222-2222-2222-222222222222',
- orderNo: 'ORDER0001',
- products: [
- { name: 'A', price: 1000, count: 1, variantId: '1' },
- { name: 'B', price: 50, count: 10, variantId: '2' },
- ],
- payment: { name: 'Credit Card', price: 10 },
- shipment: { name: 'CVS', price: 100 },
- discount: { name: 'Summer Discount', price: 50 },
- };
- const db = {};
- const order = await createOrder({
- input,
- orderRepo: new OrderRepository(db),
- });
- await cancelOrder({ id: order.id, orderRepo: new OrderRepository(db) });
- }
- entryPoint();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement