The article series
Introduction
In articles 1-6, we finished the various types of associations between entity classes and inheritance between entity classes. The example to illustrate these ideas had been the sample ECommerce scenarios. We have reached a point where shipping has to be done for the product. In this seventh article and its client code, we will demonstrate that the classes we have put forth to incorporate the shipping functionality will allow shipment items of one order to be shipped in parts by multiple shipping services. This is a flexibility that provides a business advantage. Also possible is the case that multiple orders belonging to the same customer can be shipped in one shipping. All this is possible because we have modeled the relationship between entities Shipping
and PaymentApprovedOrder
as a Many-To-Many association with OrderShipment
as an association class. This article and the client code will also demonstrate that the way in which we add ShipmentItem
i.e., the item to be shipped for a paid order will capture the material movement in the ecommerce domain.
Background
All required background covered in Articles 1 - 6.
Note: The two projects included are ECommerce1 and ECommerce 2. ECommerce1 is for samples in articles 1 - 3 and Ecommerce 2 is for the rest of the samples till this article 7. The client project to test the classes and their associations for each article is also provided and each article's test code is clearly named like "Test Code for Article 1" etc. While starting a subsequent article, the previous article's test code is commented out in the test client project and the classes in the main project would have evolved and changed to fit the subsequent scenarios of the next article. The commented out previous article's test code has not been updated to reflect changes to the class. So the test code will compile and work correctly for the most recent article for each project, i.e., article 3 for ECommerce1 and articles 7 and 6 for Ecommerce 2 which completes our ECommerce samples. Follow the instructions given earlier for using the projects with Microsoft Visual Studio 2012, though we all know how to get it done without any help.
Using the code
Code Sample for Ecommerce Scenario
Here we will briefly explain and complete the ShipmentItem scenario and demonstrate the points articulated in Introduction. The scenario is, Once a customer gives the paid order to the ecommerce site, selecting his preferred method of Shipping, the Ecommerce Site Shipping Department, will process the order and fulfill the shipping of the every item in the paid order to the customer.
So, when a paid order is created by the customer, the customer selects fast shipping or parcel service according to which an instance of FastShipping
or ParcelService
is created and stored in the base class reference Shipping
. This Shipping reference stores the paid order ID to which it is attached in the PrimaryPaidOrderId property and is persisted to the database. Not a foreign key but just a property (because Shipping
and PaymentApprovedOrder
are bound by the association class OrderShipment
). When Shipping information has to be input by a personnel of the Shipping department, they input this primary paid order ID (PRIMARYPAIDORDERID
) to which shipping has to be done to get a particular shipping instance. How do they know this PRIMARYPAIDORDERID
?
This is the the paid order ID (i.e., the ID of a new order - PAYMENTAPPROVEDORDERID
) for which a shipping has not been done which is available for the application to retrieve and show in a listbox from which the shipping department can select one. So using this, the particular shipping instance for that primary paid order ID, stored in the repository while an order is created by a customer, is retrieved from the repository and shipping information is added to it and persisted again into the repository. If the shipping department decides to do the Shipping in parts, i.e., more than one shipping for a particular order, then for subsequent parts they do not need to add the primary paid order ID. They can just create new instances of Shipping
giving all the shipping information. The OrderShipment
instance will associate Shipping
and PaymentApprovedOrder
anyway. This design will provide maximum flexibility to the business scenario.
The code in ECommerceSellerSystem for AddShippingInformation
is:
public Shipping AddShippingInformation(bool is_primary, bool is_fast,
long prime_id, string shipping_name, DateTime shipping_start_date,
string tracking_number,bool is_gift_wrapped)
{
Shipping shipping = null;
IRepository<Shipping> shipping_repo = new DBRepository<Shipping>();
NHibernate.ISession session = null;
NHibernateHelper nh = NHibernateHelper.Create();
session = nh.Session;
if (is_primary)
{
string query_string =
"from Shipping shipping1 where shipping1.PrimaryPaidOrderId = :prime_id";
shipping = session.CreateQuery(query_string).SetParameter<long>(
"prime_id",prime_id).UniqueResult<Shipping>();
if (shipping != null)
{
shipping.ShippingName = shipping_name;
shipping.ShippingStartDate = DateTime.Today;
if (shipping is FastShipping)
((FastShipping)shipping).TrackingNumber = tracking_number;
}
}
else
{
if (is_fast)
{
shipping = new FastShipping { ShippingName=shipping_name,
ShippingStartDate=DateTime.Today, PrimaryPaidOrderId=null,
TrackingNumber=tracking_number };
}
else
{
shipping = new ParcelService { ShippingName = shipping_name,
ShippingStartDate = DateTime.Today,
PrimaryPaidOrderId = null, IsGiftWrapped = is_gift_wrapped };
}
}
if (shipping != null)
{
using (NHibernate.ITransaction trans = session.BeginTransaction())
{
session.SaveOrUpdate(shipping);
trans.Commit();
}
}
return shipping;
}
As mentioned before, the association class for the Many-To-Many association between Shipping
and PaymentApprovedOrder
is OrderShipment
. Each instance of OrderShipment
will also contain the list of items that was ordered by a customer and shipped in that shipment i.e., collection of ShipmentItem
s. But if the shipping department of the ECommerce site decides to send a particular order's items in parts, i.e., first few items in one shipping instance and remaining in other shipping instances, then accordingly multiple instances of Shipping
and OrderShipment
will be created and each instance of OrderShipment
will contain the collection of shipment items shipped in it. The code in ECommerceSellerSystem for AddingShipmentItem
is:
public void AddShipmentItem(OrderShipment order_shipment, string inventory_serial_code)
{
NHibernateHelper nh = NHibernateHelper.Create();
using (NHibernate.ISession session = nh.Session)
{
string query_string = "from Item item1 where item1.InventorySerialCode = :code";
Item inventory_item = session.CreateQuery(query_string).SetParameter<string>(
"code", inventory_serial_code).UniqueResult<Item>();
if (inventory_item != null)
{
IRepository<Item> item_repository = new DBRepository<Item>();
IRepository<OrderShipment> order_shipment_repository = new DBRepository<OrderShipment>();
using (NHibernate.ITransaction trans = session.BeginTransaction())
{
inventory_item.PaidOrder.PaidOrderItems.Remove(inventory_item);
session.Delete(inventory_item);
order_shipment.ShipmentItems.Add(
new ShipmentItem { InventorySerialCode = inventory_item.InventorySerialCode });
session.SaveOrUpdate(order_shipment);
trans.Commit();
}
}
}
}
The important thing to note about the above piece of code is how it captures the domain's material movement. When an item is ordered and shipped, it no longer exists in the inventory of items. The code written above captures the domain exactly. The item moves from the inventory of the item represented by the ITEM table to the SHIPMENTITEMS table. A flag in the ITEM table would have been enough but removing the sold and shipped ones from the ITEM table and moving them to the SHIPMENTITEMS table makes it easier for any scrutiny and adds clarity. The client code to test is given by:
ECommerceSellerSystem seller_system3 = new ECommerceSellerSystem();
Shipping shipping1 = seller_system3.AddShippingInformation(true, true,
pay_order1.PaymentApprovedOrderId, "Noble House Ships",
DateTime.Today, "A101A101", false);
OrderShipment order_shipment1 = seller_system3.CreateOrderShipment(pay_order1, shipping1);
seller_system3.AddShipmentItem(order_shipment1, "00A0110");
seller_system3.AddShipmentItem(order_shipment1, "01A0101");
Shipping shipping2 = seller_system3.AddShippingInformation(false, true,
pay_order1.PaymentApprovedOrderId, "Fast Service", DateTime.Today, "A10101A", false);
OrderShipment order_shipment2 = seller_system3.CreateOrderShipment(pay_order1, shipping2);
seller_system3.AddShipmentItem(order_shipment2, "02A10101");
seller_system3.AddShipmentItem(order_shipment2, "03A01010");
seller_system3.AddShipmentItem(order_shipment2, "04A101010");
Figure 1 below shows the tables created by the client code. The top one is the SHIPMENTITEMS table and the centre one is the SHIPPING Table and bottom one is the ITEM table. Note that the second row of the SHIPPING table (center table) has nulls. Nulls are there because we have not given the shipping information for it in the client code and the row was created when the customer created the order as shown in Article 6. Compare it with Row 1 for which we gave all the shipping information in the client code above. Also note that in the SHIPPING table (center), for the third row with SHIPPINGID = 3, PRIMARYPAIDORDERID is null because this row is not created when the customer creates the paid order. This row gets created by the shipping department of the Ecommerce site which decides to send PAYMENTAPPROVEDORDER=1 in two parts and hence adds this shipping instance by providing shipping info. As mentioned before, PRIMARYPAIDODERID is filled up by the application only when the customer gives shipping preferences while creating a paid order. Also note in the top SHIPMENTITEMS table, that all items are of the same paid order but belong to two different shipping instances each representing a different shipping firm (see the ShippingID column and PAYMENTAPPROVEDORDERID columns in the topmost SHIPMENTITEM table). This is the flexibility that we mentioned before. Also note that when these shipment items are moved to the SHIPMENTITEMS table, they cease to exist in the inventory i.e., ITEM table (bottom table in figure 1).
Figure 1 - Top - SHIPMENTITEMS table, Center - SHIPPING Table, Bottom - ITEM Table
Points of Interest
Conclusion
The samples are created keeping two core principles in mind: simplistic (KISS) and minimalistic (YAGNI). The entity classes have been factored keeping high cohesion in mind but obviously they cannot have low coupling because the entire article series is for explaining various entity associations and there have been bidirectional associations used for most part to illustrate the ideas. This article completes the various scenarios of ECommerce samples. The next article of this article series will discuss the lazy loading feature in NHibernate. Enjoy NHiberante.