← Back to blog

Package-First Architecture: Why Order Status Should Be Derived, Not Assigned

· Stoa Logistics
architecture fulfillment engineering

Package-First Architecture: Why Order Status Should Be Derived, Not Assigned

Most order management systems track order status directly. Someone clicks “shipped” and the order is marked shipped. Simple, right?

Until you have an order with three packages. Two shipped yesterday, one ships today. Is the order “shipped”? “Partially shipped”? When did it ship?

This is why Vectis uses package-first architecture. Order status is derived from package status, not assigned directly.

The Traditional Approach (And Its Problems)

In a typical OMS:

  1. Order comes in → status: pending
  2. Warehouse picks items → someone clicks a button → status: picking
  3. Items packed → someone clicks a button → status: packed
  4. Label printed → someone clicks a button → status: shipped

The order status is a single field that gets updated manually or via simple triggers. This works fine for simple orders but breaks down with:

Multi-package orders: Customer orders 10 items. You pack them in 3 boxes. When is the order “packed”? When the first box is packed? The last? What if box 2 is packed but box 1 isn’t?

Split shipments: Items come from different warehouses. Phoenix ships Monday, Atlanta ships Tuesday. The order isn’t really “shipped” until both are out the door.

Partial fulfillment: 8 items in stock, 2 backordered. You ship what you have. Is the order “shipped” or “partially shipped”? What happens when the backordered items arrive?

Repackaging: Packer realizes the box is wrong, needs to repack. In a traditional system, you’re fighting the status model.

The Package-First Approach

In Vectis, packages are first-class entities with their own lifecycle:

pending → allocated → sent_to_wms → picking → picked → 
packing → packed → shipped → in_transit → out_for_delivery → delivered

Plus exception states: failed_delivery, returned, cancelled

Each package tracks its own status independently. The order status is then computed from its packages:

  • All packages pending? Order is pending
  • Any package picking? Order is picking
  • All packages shipped? Order is shipped
  • Some shipped, some not? Order is partially_shipped
  • All packages delivered? Order is delivered

Why This Matters

Accuracy: The order status always reflects reality. You can’t have an order marked “shipped” when a package is still sitting in the warehouse.

Auditability: Every status change is tied to a specific package. “Order moved to shipped because package PKG-789 was scanned at FedEx.”

Flexibility: Need to repack? Change the package status. Need to split a package? Create two packages, each with their own lifecycle. The order status updates automatically.

Multi-warehouse: Each warehouse ships its packages independently. The order status reflects the aggregate state without manual coordination.

Implementation Details

In Vectis, the order status computation looks roughly like this:

function deriveOrderStatus(packages: Package[]): OrderStatus {
  if (packages.every(p => p.status === 'delivered')) return 'delivered';
  if (packages.every(p => p.status === 'shipped' || p.status === 'delivered')) return 'shipped';
  if (packages.some(p => ['shipped', 'in_transit', 'delivered'].includes(p.status))) return 'partially_shipped';
  if (packages.every(p => p.status === 'packed')) return 'packed';
  // ... and so on
}

This runs on every package status change, keeping the order status in sync automatically.

The Tradeoff

Package-first architecture adds complexity. You’re managing more entities, more status transitions, more edge cases. For simple operations (single warehouse, single package per order), it’s overkill.

But if you’re doing any of the following, the complexity pays for itself:

  • Multi-package orders
  • Split shipments across warehouses
  • Partial fulfillment with backorders
  • High-volume operations where manual status updates don’t scale

Conclusion

Order status should describe reality, not intent. When you derive status from packages, you get accurate tracking that handles real-world complexity without manual intervention.


Vectis uses package-first architecture with a 14-state package lifecycle. Learn more about how it works.

Ready to simplify your operations?

See how Vectis can help your team.

Request a demo