Exowpee is a monorepo with eight packages inside packages/. Each package owns a specific business domain and communicates with others through contracts (PHP interfaces). This page shows you the full picture.

The Package Map

What Each Package Owns (One-Liner)

How Packages Communicate: Contracts

Packages never import each other’s classes directly. Instead, they depend on interfaces (contracts): Each package exposes an interface in its src/Contracts/ directory. Other packages depend on the interface, not the implementation. Laravel’s service container binds the real class at runtime. Example: When the Order package needs to create an accounting entry after a sale, it does not import LedgerService directly. It uses LedgerInterface:
Why this matters: You can work on the Order package without understanding the Ledger internals. The interface is the contract — as long as it is respected, everything works.

The Request Journey: What Happens When Sena Makes a Sale

Let us trace POST /v1/order/orders from HTTP request to database writes, showing which packages are involved at each step.

Packages involved in a single sale:

This is why understanding the package map matters — even a “simple” sale touches 4 out of 8 packages.

Dependency Rules

These rules keep the codebase maintainable:
  1. Shared has no dependencies — it is the foundation layer
  2. IAM depends only on Shared — authentication is a foundational concern
  3. Team depends on IAM + Shared — organizational structure needs identity
  4. Catalog, Order, Customer depend on IAM + Team + Shared — business logic needs both identity and company context
  5. Ledger depends on IAM + Team + Shared — accounting needs identity and company context
  6. Order depends on Catalog — orders reference products and manage stock
  7. Order and Customer depend on Ledger — financial events create accounting entries
  8. Notification depends on Shared — handles email sending (invitations, onboarding, verification, password reset)
  9. No circular dependencies — if A depends on B, B must not depend on A

Common Mistakes

Importing a class from another package directly — Always use the contract interface. If you need something from the Ledger package, inject LedgerInterface, not LedgerService. Creating circular dependencies — If Package A depends on Package B, then Package B must not depend on Package A. If you need two-way communication, use events or a shared interface in the Shared package. Forgetting that Catalog is organization-scoped — Products, categories, and vendors are shared across all companies. If you add a company-specific filter to a catalog query, you will break multi-branch visibility. Forgetting that Stock is company-scoped — Stock levels are per company, not per organization. If you aggregate stock without grouping by company, the numbers will be wrong.

Where to Go Next


Previous: Permissions Explained | Next: Open your shop