Many-to-Many Relationship using Entity Framework or Java JPA.
Design Decision: Final Vendor ↔ Item (Many-to-Many)Relationship in GoProcure (Association + Composition)
Introduction
As we continue building the GoProcure enterprise procurement system, one of the most crucial design decisions we had to revisit was the relationship between Vendor and Item.
At first glance, it might seem simple — “a Vendor supplies many Items.” But in real-world enterprise procurement, this relationship is more nuanced.
In this article, I’ll walk you through the reasoning behind our final design, why we changed it, and how we implemented it in both .NET (EF Core) and Java (JPA/Hibernate).
Understanding the Procurement Context
In GoProcure, the Item Catalog represents all products or materials that may be needed across various departments.
-
These items belong to the organization, not to any specific Vendor.
-
Vendors can supply multiple Items.
-
Each Item can be supplied by multiple Vendors, possibly at different prices, lead times, or quality ratings.
-
The Vendor selection happens when a Purchase Request (PR) or Purchase Order (PO) is created, not when the Item is defined.
This understanding shifted our perspective on how to model these relationships.
Our Initial Design (and Why We Changed It)
Initially, we modeled Vendor → Item as a One-to-Many Aggregation. The idea was that a Vendor “owned” its Items.
However, this quickly proved incorrect when we considered scenarios like:
-
Multiple Vendors supplying the same product (e.g., “Office Chair”).
-
Reassigning vendors without recreating items.
-
Retaining historical purchase data even if a vendor is deactivated.
The initial model caused unnecessary coupling between Vendors and Items. So, we revised it into a cleaner, more flexible structure.
Final Design Summary
| Relationship | Type | Cardinality | Ownership | Delete Behavior |
|---|---|---|---|---|
| Vendor ↔ Item | Association | Many-to-Many | None | Independent |
| Vendor ↔ VendorItem | Composition | One-to-Many | Vendor owns VendorItem | Cascade Delete |
| Item ↔ VendorItem | Association | One-to-Many | None | Restrict |
Explanation:
-
VendorandItemare independent aggregates. -
VendorItemis a linking entity (association class) that captures the relationship details such asprice,delivery period,availability, andrating. -
When a
Vendoris deleted, all itsVendorItemsgo with it (composition). -
However, the
Itemsremain in the catalog (association).
UML Representation
-
◆ = Composition (Vendor owns VendorItem)
-
o = Association (Item is linked via VendorItem)
This clearly illustrates that Vendor and Item are independent, connected through the associative entity VendorItem.
Implementation in .NET (EF Core)
Explanation:
-
The composite key (
VendorId,ItemId) ensures each Vendor-Item pair is unique. -
Cascading deletes from Vendor ensure dependent VendorItems are removed automatically.
-
Restrict delete on Item prevents losing Vendor history when removing an Item.
Implementation in Java (JPA / Hibernate)
Explanation:
-
VendorItemuses a composite key (VendorItemId) to link Vendor and Item. -
@MapsIdmaps the embedded key fields to the respective foreign keys. -
Cascading rules are applied via
@OneToMany(mappedBy = ..., cascade = CascadeType.ALL)inVendor.
Why This Design Matters
✅ Data Integrity — prevents unintended deletions while keeping relationships consistent.
✅ Flexibility — supports multiple Vendors per Item and vice versa.
✅ Scalability — easily extended to include attributes like rating, minOrderQty, or contractExpiry.
✅ Realism — accurately models enterprise procurement processes.
Key Takeaways
-
Relationships must reflect business semantics, not just data structure.
-
A Vendor supplying Items is association, not ownership.
-
The lifecycle of linked entities (like
VendorItem) defines whether it’s composition or aggregation. -
Both EF Core and JPA allow us to implement these relationships cleanly with proper delete behavior and composite keys.
Next Steps
In the next part of this series, we’ll begin implementing the Domain Models as Classes in .NET and Java, while adhering to best practices in Domain-Driven Design and Clean Architecture.
Stay tuned 🚀
Follow the #CodeTrip journey to learn how we turn great design into production-ready code.

