Once you have a starting point, you will need to identify a path through the graph. It helps to have an image of the graph handy so you can map out your journey.
Let's use the enterprise application as an example. Here is a map of the model related to carts.
That's quite a complicated model. But we can follow the map to find a path. Suppose that the buyer is logged into an environment. They want to locate all of their carts.
The path has two starting facts: the Environment
and the logged-in User
.
It ends at the Cart
.
Along the way it passes through Customer.Buyer
and Customer
.
Label each of these facts:
Now write a specification that joins these labels.
var cartsInEnvironmentForUser = Given<Environment, User>.Match((environment, user, facts) =>
from customerBuyer in facts.OfType<CustomerBuyer>()
where customerBuyer.buyer == user
from customer in facts.OfType<Customer>()
where customerBuyer.customer == customer
where customer.environment == environment
from cart in facts.OfType<Cart>()
where cart.customer == customer
select cart);
Keep this path as short as possible. You might want to select more information in the results. For example, you might want the customer name, the names of products in the cart, and the quantities requested. Get that additional information in a projection, not by adding more facts to the path.
The specification will return one result for each matching combination of facts. If you include cart items, for example, then you would multiply the number of results returned. Worse yet, you would eliminate carts that didn't yet have any items. Projections allow you to query nested specifications.