Order operations such as placing, cancelling, and refunding orders require different logic. Without the Command pattern, there is no way to maintain a history, undo a recent action, or queue operations for later execution.
The Command pattern wraps each operation in a self-contained command object that implements execute() and undo(). An invoker service manages execution, maintains a stack for undo support, and records a full history log.
Each command tracks execution state to prevent double-execution. The invoker maintains a stack for undo operations and a full history log:
command.interface.ts place-order.command.ts cancel-order.command.ts refund-order.command.ts order-command-invoker.service.ts
export interface CommandResult {
success : boolean ;
message : string ;
data ? : Record < string , any >;
}
export interface Command {
readonly name : string ;
execute (): CommandResult ;
undo (): CommandResult ;
} import { Command , CommandResult } from './command.interface' ;
export class PlaceOrderCommand implements Command {
readonly name = 'PlaceOrder' ;
private executed = false ;
constructor (
private readonly orderId : string ,
private readonly amount : number ,
) {}
execute (): CommandResult {
if ( this . executed ) {
return {
success : false ,
message : `Order ${ this . orderId } has already been placed` ,
};
}
this . executed = true ;
return {
success : true ,
message : `Order ${ this . orderId } placed successfully` ,
data : {
orderId : this . orderId ,
amount : this . amount ,
status : 'placed' ,
timestamp : new Date (). toISOString (),
},
};
}
undo (): CommandResult {
if ( ! this . executed ) {
return {
success : false ,
message : `Order ${ this . orderId } was never placed — cannot undo` ,
};
}
this . executed = false ;
return {
success : true ,
message : `Order ${ this . orderId } placement reversed` ,
data : {
orderId : this . orderId ,
status : 'reversed' ,
timestamp : new Date (). toISOString (),
},
};
}
} import { Command , CommandResult } from './command.interface' ;
export class CancelOrderCommand implements Command {
readonly name = 'CancelOrder' ;
private executed = false ;
constructor (
private readonly orderId : string ,
private readonly reason ? : string ,
) {}
execute (): CommandResult {
if ( this . executed ) {
return {
success : false ,
message : `Order ${ this . orderId } has already been cancelled` ,
};
}
this . executed = true ;
return {
success : true ,
message : `Order ${ this . orderId } cancelled successfully` ,
data : {
orderId : this . orderId ,
reason : this . reason ?? 'No reason provided' ,
status : 'cancelled' ,
timestamp : new Date (). toISOString (),
},
};
}
undo (): CommandResult {
if ( ! this . executed ) {
return {
success : false ,
message : `Order ${ this . orderId } was never cancelled — cannot undo` ,
};
}
this . executed = false ;
return {
success : true ,
message : `Order ${ this . orderId } cancellation reversed — order restored` ,
data : {
orderId : this . orderId ,
status : 'restored' ,
timestamp : new Date (). toISOString (),
},
};
}
} import { Command , CommandResult } from './command.interface' ;
export class RefundOrderCommand implements Command {
readonly name = 'RefundOrder' ;
private executed = false ;
constructor (
private readonly orderId : string ,
private readonly amount ? : number ,
private readonly reason ? : string ,
) {}
execute (): CommandResult {
if ( this . executed ) {
return {
success : false ,
message : `Refund for order ${ this . orderId } has already been processed` ,
};
}
this . executed = true ;
return {
success : true ,
message : `Refund for order ${ this . orderId } processed successfully` ,
data : {
orderId : this . orderId ,
refundAmount : this . amount ?? 0 ,
reason : this . reason ?? 'No reason provided' ,
status : 'refunded' ,
timestamp : new Date (). toISOString (),
},
};
}
undo (): CommandResult {
if ( ! this . executed ) {
return {
success : false ,
message : `No refund was issued for order ${ this . orderId } — cannot undo` ,
};
}
this . executed = false ;
return {
success : true ,
message : `Refund for order ${ this . orderId } reversed — funds reclaimed` ,
data : {
orderId : this . orderId ,
reclaimedAmount : this . amount ?? 0 ,
status : 'refund_reversed' ,
timestamp : new Date (). toISOString (),
},
};
}
} import { Injectable } from '@nestjs/common' ;
import { Command , CommandResult } from './command.interface' ;
export interface HistoryEntry {
commandName : string ;
action : 'execute' | 'undo' ;
result : CommandResult ;
timestamp : string ;
}
@ Injectable ()
export class OrderCommandInvokerService {
private readonly executedCommands : Command [] = [];
private readonly history : HistoryEntry [] = [];
executeCommand ( command : Command ): CommandResult {
const result = command . execute ();
if ( result . success ) {
this . executedCommands . push ( command );
}
this . history . push ({
commandName : command . name ,
action : 'execute' ,
result ,
timestamp : new Date (). toISOString (),
});
return result ;
}
undoLastCommand (): CommandResult {
const command = this . executedCommands . pop ();
if ( ! command ) {
return {
success : false ,
message : 'No commands to undo' ,
};
}
const result = command . undo ();
this . history . push ({
commandName : command . name ,
action : 'undo' ,
result ,
timestamp : new Date (). toISOString (),
});
return result ;
}
getHistory (): HistoryEntry [] {
return [... this . history ];
}
}
command.controller.ts command.service.ts command.module.ts
import { Controller , Post , Body , Get } from '@nestjs/common' ;
import { OrderService } from './integration.service' ;
@ Controller ( 'orders' )
export class OrderController {
constructor ( private readonly orderService : OrderService ) {}
@ Post ()
placeOrder (@ Body () body : { orderId : string ; amount : number }) {
return this . orderService . placeOrder ( body . orderId , body . amount );
}
@ Post ( 'cancel' )
cancelOrder (@ Body () body : { orderId : string ; reason : string }) {
return this . orderService . cancelOrder ( body . orderId , body . reason );
}
@ Post ( 'undo' )
undo () {
return this . orderService . undo ();
}
@ Get ( 'history' )
getHistory () {
return this . orderService . getHistory ();
}
} import { Injectable } from '@nestjs/common' ;
import { OrderCommandInvokerService } from './order-command-invoker.service' ;
import { PlaceOrderCommand } from './place-order.command' ;
import { CancelOrderCommand } from './cancel-order.command' ;
@ Injectable ()
export class OrderService {
constructor ( private readonly invoker : OrderCommandInvokerService ) {}
placeOrder ( orderId : string , amount : number ) {
return this . invoker . executeCommand (
new PlaceOrderCommand ( orderId , amount ),
);
}
cancelOrder ( orderId : string , reason : string ) {
return this . invoker . executeCommand (
new CancelOrderCommand ( orderId , reason ),
);
}
undo () {
return this . invoker . undoLastCommand ();
}
getHistory () {
return this . invoker . getHistory ();
}
} import { Module } from '@nestjs/common' ;
import { OrderController } from './integration.controller' ;
import { OrderService } from './integration.service' ;
import { OrderCommandInvokerService } from './order-command-invoker.service' ;
@ Module ({
controllers : [ OrderController ],
providers : [ OrderService , OrderCommandInvokerService ],
exports : [ OrderCommandInvokerService ],
})
export class CommandModule {}