Seven-service payment processor
55 / 100A single method orchestrates seven services, creating a structural coupling trap where any change to payment, fraud, inventory, or notification touches the same method.
▸ Input code
class PaymentProcessor {
constructor(
private db, private stripe, private mailer, private inventory,
private analytics, private taxCalc, private fraudDetection
) {}
async processPayment(orderId, cardToken) {
const order = await this.db.orders.findById(orderId);
const tax = this.taxCalc.calculate(order.items, order.shippingAddress.state);
order.tax = tax;
const fraudScore = await this.fraudDetection.evaluate({ amount: order.total + tax, card: cardToken, ... });
if (fraudScore > 0.8) { await this.mailer.send(...); await this.analytics.track('fraud_hold', ...); return {...}; }
const charge = await this.stripe.charges.create({ amount: Math.round((order.total + tax) * 100), ... });
for (const item of order.items) {
await this.inventory.decrement(item.sku, item.quantity);
if (await this.inventory.getStock(item.sku) < 10) { await this.mailer.send('warehouse@company.com', ...); }
}
order.status = 'paid'; order.chargeId = charge.id; await this.db.orders.save(order);
await this.mailer.send(order.customerEmail, 'Payment Received', `Charge: $${order.total + tax}`);
await this.analytics.track('payment_success', { orderId, amount: order.total + tax });
return { status: 'paid', chargeId: charge.id };
}
}
🔴 Change Propagation — Seven-service constructor signals a God Class
🔴 Change Propagation — Inventory loop embeds warehouse notification policy
< 10) fires a warehouse email with hardcoded recipient inside the payment loop.StockLevelChanged domain event a separate notifier subscribes to.🟡 Knowledge Duplication — order.total + tax computed three times
order.grandTotal.🟡 Domain Model Distortion — Order is a mutable data bag
order.tax, order.status, order.chargeId are all set externally; the object holds no behaviour.Order state-transition methods: order.recordPayment(chargeId).