Domain Modeling
Here are a few key concepts related to domain modeling and how they are applied within the platform.
Note: It's important to keep in mind these are guidelines, and are not followed completely or religiously. Instead, think of them as heuristics when reading the code or extending elements of the platform.
Bounded Contexts
With large domains (ecommerce is certainly one of these), it becomes difficult to track the intricacies of how models relate to one another. Therefore, Workarea separates these models into different contexts as a way to break down the bigger picture into more manageable pieces. Each context addresses a single problem or set of problems within the larger system. This technique reduces coupling along domain lines and allows functionality to change independently.
To see this in Workarea, look at how the app/models directory in Workarea Core is divided. Each subdirectory is a bounded context, such as the following:
CatalogContentFulfillmentInventoryNavigationOrderPaymentPricingRecommendationShippingTaxUser
Orders Example
From a customer's perspective, an "order" might contain the following elements:
- Products
- Prices
- Shipping method
- Credit card
By handling these problems separately, the code/modeling/logic for each is much simpler and less coupled. For instance, logic around what shipping methods are available has no coupling to putting together the pricing for the order, even though shipping methods will always have and depend on a price.
However, to present the order to the customer, Workarea pulls:
- Some products from the
Catalog::Productclass - Pricing for the order items from the
Pricingengine - Available shipping methods from the
Shipping::Methodclass - A credit card from the
Payment
This is the responsibility of the view model layer (and one of the reasons it exists).
Pricing Example
In another example, pricing from a business user's perspective probably includes several elements:
- SKU price
- Discounts
- Shipping costs
- Taxes
This can get quite complicated, so it is effective to break the problem apart:
- SKU price - stored and calculated in the
Pricingengine - Discounts - stored and calculated in the
Pricingengine - Shipping costs - stored and calculated by the
Shippingmodule and totalled by thePricingengine - Taxes - rates are stored calculated by the
Taxmodule, but applied by thePricingengine
This design makes it much easier to swap out how shipping or taxes are determined. These can be significant problems in their own right, and many implementations use third parties for these purposes.
Shared Models
There are some things that the contexts share. Changing these classes may/will require cooperation from a potentially broad range of classes, so it's a risky move.
Examples of classes that are shared include:
Address- generically models an addressPriceAdjustment- represents an adjustment to price, used for tracking/calculating pricesRegionandCountry- models countries and subdivisions of countriesShippingOption- models a shipping method selectable or selected by a user
So PriceAdjustment is used by the Pricing engine to create the pricing on the Order, Shipping::Shipment, etc.
Help Us Improve this Doc
Was this helpful? Open a GitHub issue to report a problem with this doc, suggest an improvement, or otherwise provide feedback. Thanks!