Skip to content

Serialisers and Aggregates

AndyHitchman edited this page Oct 13, 2010 · 2 revisions

Here are two very simple classes that represent aggregate roots (in the Domain-driven design sense), and some indexes on these objects. Note that Customer references a set of Orders and Order references a Customer.

[Serializable]
public class Customer
{
    public Customer()
    {
        Orders = new List<Order>();
    }

    public int Number { get; set; }
    public string Name { get; set; }
    public IList<Order> Orders { get; set; }
}

/// <summary>
/// Order is another distinct aggregate root.
/// </summary>
[Serializable]
public class Order
{
    public int Number { get; set; }
    public Customer Customer { get; set; }
}

public class CustomersByNumber : IIndex<Customer, int>
{
    /// <summary>
    ///   For each Customer we yield one value for the customer number.
    /// </summary>
    /// <param name = "customer"></param>
    /// <returns></returns>
    public IEnumerable<int> Yield(Customer customer)
    {
        yield return customer.Number;
    }
}

public class OrdersByNumber : IIndex<Order, int> 
{
    public IEnumerable<int> Yield(Order graph)
    {
        yield return graph.Number;
    }
}

public class OrdersByCustomerNumber : IIndex<Order,int> 
{
    public IEnumerable<int> Yield(Order graph)
    {
        yield return graph.Customer.Number;
    }
}

When we register the Customer and Order graphs, we use the AggregateBinarySerializer. This causes all references to other registered graphs to be serialised as references in the backing store.

For example, the member Order.Customer will be serialised using the Stash internal id for the customer, rather than a copy of the Customer instance. When deserialising, the Customer instance will be added to the session, if it isn’t already being tracked, by deserialising it from the backing store. Lazy loading is not yet implemented, but is planned.

//Kickstart Stash by registering Customer and Order as a persistent graphs using the AggregateBinarySerializer.
Kernel.Kickstart(
    new BerkeleyBackingStore(new DefaultBerkeleyBackingStoreEnvironment(TempDir)),
    register =>
        {
            register.Graph<Customer>(_ => _.SerializeWith(new AggregateBinarySerializer<Customer>(_.RegisteredGraph)));
            register.Graph<Order>(_ => _.SerializeWith(new AggregateBinarySerializer<Order>(_.RegisteredGraph)));
            register.Index(new CustomersByNumber());
            register.Index(new OrdersByNumber());
            register.Index(new OrdersByCustomerNumber());
        });

When you persist a customer, the list of references to the orders is persisted. The opposite reference from Order to Customer is also persisted as a reference. The association does not need to be navigable in both directions. Stash does nothing to maintain these associations for you. Your model is responsible for this behaviour.

Also, Stash must be explicitly told to endure both the customer and the order. It will not persist an order on your behalf because it is referenced by a customer that you have endured.

var customer = new Customer {Number = 1, Name = "Acme Bolts"};
var order1 = new Order {Customer = customer, Number = 1001};
var order2 = new Order {Customer = customer, Number = 1002};
var order3 = new Order {Customer = customer, Number = 1003};

customer.Orders.Add(order1);
customer.Orders.Add(order2);
customer.Orders.Add(order3);

session.Endure(customer);
session.Endure(order1);
session.Endure(order2);
session.Endure(order3);

session.Complete();

When we fetch these graphs back out of Stash, we can see that they are reference equal.

var customersStash = session.GetStashOf<Customer>();
var ordersStash = session.GetStashOf<Order>();

var order1 = ordersStash.Matching(_ => _.Where<OrdersByNumber>().EqualTo(1001)).Single();
var customer = customersStash.Matching(_ => _.Where<CustomersByNumber>().EqualTo(1)).Single();
var order2 = ordersStash.Matching(_ => _.Where<OrdersByNumber>().EqualTo(1002)).Single();

customer.Orders.ShouldContain(order1);
customer.Orders.ShouldContain(order2);
customer.Orders.ShouldContain(order3);

order1.Customer.ShouldBeTheSameAs(customer);
order2.Customer.ShouldBeTheSameAs(customer);
Clone this wiki locally