134
The uniform interface is 42 (exclusively for Orleans Virtual Meetup)

The uniform interface is 42

Embed Size (px)

Citation preview

The uniform

interface is 42(exclusively for Orleans Virtual Meetup)

About me

Preface

sergeybykov commented on Jan 29

@yevhen do we have a date set for your

presentation yet? Looks like we might want to

dedicate a part of it to this interesting philosophical

discussion. :-)

jkonecki commented on Dec 9, 2014

I would like to hear @yevhen talk about his

Orleans.Bus project and Orleans patterns / best

practices.

MS SQL

iPhone6 (100pcs)

Buyers Sellers

HoloLens (10pcs)

Pebble(200pcs)

PetCube(122pcs)

XBOX1(1500pcs)

eCommerce (Inventory)

check-out 10

check-out 50

check-out

check-in 100

create

deactivate

AppleWatch (10000pcs)

rename (“iWatch”)

Use-Cases

• Create item

• Rename item

• Deactivate item

• Add to stock (quantity)

• Order item (quantity)

Buyers Sellers

Both

• Get item details (name, quantity in-stock, active?)

Orleans Interface Project

using System;using System.Linq;using System.Threading.Tasks;

using Orleans;

namespace Example{

[ExtendedPrimaryKey]public interface IInventoryItemGrain : IGrain{

Task Create(string name);Task Rename(string newName);Task CheckIn(int qty);Task CheckOut(int qty);Task Deactivate();Task<InventoryItemDetails> GetDetails();

}

[Serializable]public class InventoryItemDetails{

public string Name;public int Total;public bool Active;

}}

public interface IInventoryItemGrainState : IGrainState{

string Name { get; set; }int Qantity { get; set; }bool Active { get; set; }

}

[StorageProvider(ProviderName = "SqlStore")]public class InventoryItemGrain : Grain<IInventoryItemGrainState>, IInventoryItemGrain{

public Task Create(string name){

…}

public Task CheckOut(int qty){

…}

public Task Deactivate(){

… }

…}

Orleans Implementation Project

Create Item

public Task Create(string name){

if (string.IsNullOrEmpty(name))throw new ArgumentException("Inventory item name cannot be null or empty");

if (State.Name != null)throw new InvalidOperationException(

string.Format("Inventory item with id {0} has been already created", this.GetPrimaryKey()));

State.Name = name;State.Active = true;

return State.WriteStateAsync();}

Deactivate Item

public Task Deactivate(){

if (!State.Active)throw new InvalidOperationException(

this.GetPrimaryKey() + " item is deactivated");

State.Active = false;

return State.WriteStateAsync();}

Check-out

public Task CheckOut(int qty){

if (qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (State.Qty - qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

State.Qty -= qty;

return State.WriteStateAsync();}

The issue

public Task Create(string name){

// some business logic ...

return State.WriteStateAsync();}

public Task CheckOut(int qty){

// some business logic ...

return State.WriteStateAsync();}

public Task Deactivate(){

// some business logic ...

return State.WriteStateAsync();}

The issue

public Task Create(string name){

// some business logic ...

return State.WriteStateAsync();}

public Task CheckOut(int qty){

// some business logic ...

return State.WriteStateAsync();}

public Task Deactivate(){

// some business logic ...

return State.WriteStateAsync();}

The issue

public Task Create(string name){

// some business logic ...

return State.WriteStateAsync();}

public Task CheckOut(int qty){

// some business logic ...

return State.WriteStateAsync();}

public Task Deactivate(){

// some business logic ...

return State.WriteStateAsync();}

Duplication !!!

The issue

public Task Create(string name){

// some business logic ...

return State.WriteStateAsync();}

public Task CheckOut(int qty){

// some business logic ...

return State.WriteStateAsync();}

public Task Deactivate(){

// some business logic ...

return State.WriteStateAsync();}

Duplication !!!

Violation of DRY

Testing business logic

public Task CheckOut(int qty){

if (qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (State.Qty - qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

State.Qty -= qty;

return State.WriteStateAsync();}

Testing business logic

public Task CheckOut(int qty){

if (qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (State.Qty - qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

State.Qty -= qty;

return State.WriteStateAsync();}

Testing business logic

public Task CheckOut(int qty){

if (qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (State.Qty - qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

State.Qty -= qty;

return State.WriteStateAsync();}

[TestFixture]public class InventoryItemFixture{

[Test]public void When_checking_out_more_than_there_is_in_stock(){

var grain = new InventoryItemGrain(); Assert.Throws<InvalidOperationException>(()=> grain.CheckOut(10));

} }

Testing business logic

------ Test started: Assembly: Example.Meetup.exe ------

Test 'Example.InventoryItemFixture.When_checking_out_more_than_there_is_in_stock' failed: Expected: <System.InvalidOperationException>But was: <System.NullReferenceException> (Object reference not set to an instance of an object.)at Example.InventoryItemGrain.CheckOut(Int32 qty) in C:\Work\Source\Example.Meetup\Fixture.cs:line 62at NUnit.Framework.Assert.Throws(IResolveConstraint expression, TestDelegate code, String message,…)

Fixture.cs(110,0): at Example.InventoryItemFixture.When_checking_out_more_than_there_is_in_stock()

0 passed, 1 failed, 0 skipped, took 0.51 seconds (NUnit 2.6.2).

Testing business logic

------ Test started: Assembly: Example.Meetup.exe ------

Test 'Example.InventoryItemFixture.When_checking_out_more_than_there_is_in_stock' failed: Expected: <System.InvalidOperationException>But was: <System.NullReferenceException> (Object reference not set to an instance of an object.)at Example.InventoryItemGrain.CheckOut(Int32 qty) in C:\Work\Source\Example.Meetup\Fixture.cs:line 62at NUnit.Framework.Assert.Throws(IResolveConstraint expression, TestDelegate code, String message,…)

Fixture.cs(110,0): at Example.InventoryItemFixture.When_checking_out_more_than_there_is_in_stock()

0 passed, 1 failed, 0 skipped, took 0.51 seconds (NUnit 2.6.2).

Testing business logic

public Task CheckOut(int qty){

if (qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (State.Qty - qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

State.Qty -= qty;

return State.WriteStateAsync();}

Testing business logic

public Task CheckOut(int qty){

if (qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (State.Qty - qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

State.Qty -= qty;

return State.WriteStateAsync();}

1. Define an interface

interface IInventoryItemGrainState : IGrainState{

string Name { get; set; }int Quantity { get; set; }bool Active { get; set; }

}

Orleans’ declarative persistence

1. Define an interface

interface IInventoryItemGrainState : IGrainState{

string Name { get; set; }int Quantity { get; set; }bool Active { get; set; }

}

2. Inherit from Grain<TGrainState>

[StorageProvider(ProviderName = "SqlStore")]class InventoryItemGrain : Grain<IInventoryItemGrainState>, IInventoryItemGrain

Orleans’ declarative persistence

1. Define an interface

interface IInventoryItemGrainState : IGrainState{

string Name { get; set; }int Quantity { get; set; }bool Active { get; set; }

}

2. Inherit from Grain<TGrainState>

[StorageProvider(ProviderName = "SqlStore")]class InventoryItemGrain : Grain<IInventoryItemGrainState>, IInventoryItemGrain

3. Proxy implementation of TGrainState will be generated

Orleans’ declarative persistence

1. Define an interface

interface IInventoryItemGrainState : IGrainState{

string Name { get; set; }int Quantity { get; set; }bool Active { get; set; }

}

2. Inherit from Grain<TGrainState>

[StorageProvider(ProviderName = "SqlStore")]class InventoryItemGrain : Grain<IInventoryItemGrainState>, IInventoryItemGrain

3. Proxy implementation of TGrainState will be generated

4. At run time, the framework will create an instance of TGrainState

and set State property

Orleans’ declarative persistence

1. Define an interface

interface IInventoryItemGrainState : IGrainState{

string Name { get; set; }int Quantity { get; set; }bool Active { get; set; }

}

2. Inherit from Grain<TGrainState>

[StorageProvider(ProviderName = "SqlStore")]class InventoryItemGrain : Grain<IInventoryItemGrainState>, IInventoryItemGrain

3. Proxy implementation of TGrainState will be generated

4. At run time, the framework will create an instance of TGrainState

and set State property

Orleans’ declarative persistence

Q: But we can just set State property to a

stub within a test harness, right?

Orleans’ declarative persistence

Q: But we can just set State property to a

stub within a test harness, right?

Orleans’ declarative persistence

A: Nope, you can’t. It’s protected and

read-only. There is no setter.

[TestFixture]public class InventoryItemFixture{

[Test]public void When_checking_out_more_than_there_is_in_stock(){

var grain = InventoryItemGrainFactory.GetGrain("test-123"); Assert.Throws<InvalidOperationException>(()=> grain.CheckOut(10));

} }

Testing business logic (with real Silo)

[TestFixture]public class InventoryItemFixture{

[Test]public void When_checking_out_more_than_there_is_in_stock(){

var grain = InventoryItemGrainFactory.GetGrain("test-123"); Assert.Throws<InvalidOperationException>(()=> grain.CheckOut(10));

} }

Testing business logic (with real Silo)

------ Test started: Assembly: Example.Meetup.exe ------

Test 'Example.InventoryItemFixture.When_checking_out_more_than_there_is_in_stock' failed: Expected: <System.InvalidOperationException>But was: <System.Data.SqlClient.SqlException> (Server does not exist or connection was refused )at Example.SqlProvider.ReadStateAsync(GrainState state) in C:\Work\Source\Example.Meetup\Fixture.cs:line 8at Example.InventoryItemFixture.When_checking_out_more_than_there_is_in_stock()

0 passed, 1 failed, 0 skipped, took 5.12 seconds (NUnit 2.6.2).

[TestFixture]public class InventoryItemFixture{

[Test]public void When_checking_out_more_than_there_is_in_stock(){

var grain = InventoryItemGrainFactory.GetGrain("test-123"); Assert.Throws<InvalidOperationException>(()=> grain.CheckOut(10));

} }

Testing business logic (with real Silo)

------ Test started: Assembly: Example.Meetup.exe ------

Test 'Example.InventoryItemFixture.When_checking_out_more_than_there_is_in_stock' failed: Expected: <System.InvalidOperationException>But was: <System.Data.SqlClient.SqlException> (Server does not exist or connection was refused )at Example.SqlProvider.ReadStateAsync(GrainState state) in C:\Work\Source\Example.Meetup\Fixture.cs:line 8at Example.InventoryItemFixture.When_checking_out_more_than_there_is_in_stock()

0 passed, 1 failed, 0 skipped, took 5.12 seconds (NUnit 2.6.2).

Testing business logic (with real Silo)

[StorageProvider(ProviderName = "SqlStore")]public class InventoryItemGrain : Grain<IInventoryItemGrainState>, IInventoryItemGrain{

public Task ActivateAsync(){

State.ReadStateAsync();}

…}

Testing business logic (with real Silo)

1. Setup and run test integration silo in a test harness

Testing business logic (with real Silo)

1. Setup and run Orleans silo in a test harness

2. Setup fresh test database

Testing business logic (with real Silo)

1. Setup and run Orleans silo in a test harness

2. Setup fresh test database

3. Run migration scripts to setup db schema

Testing business logic (with real Silo)

1. Setup and run Orleans silo in a test harness

2. Setup fresh test database

3. Run migration scripts to setup db schema

4. Cleanup db before every test (isolation, repeatability)

To test a single line of domain code?

if (this.qty - qty < 0)throw new InvalidOperationException();

1. Use only full-blown integration testing

Testing business logic

1. Use only integration testing

Testing business logic

1. Use only integration testing

2. Don’t write unit-tests

Testing business logic

1. Use only integration testing

2. Don’t write unit-tests

Testing business logic

Is slow.

Unit testing business logic

Is slow.

Is hard.

Unit testing business logic

Is slow.

Is hard.

Doesn’t worth the price.

Unit testing business logic

Is slow.

Is hard.

Doesn’t worth the price.

Don’t fight the framework.

Unit testing business logic

Is slow.

Unit testing business logic

Is hard.

Doesn’t worth the price.

Don’t fight the framework.

Just write an integration test.

Separation of Concerns

Separation of Concerns

VIOLATION

OF

Different levels of abstraction

public Task CheckOut(int qty){

if (qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (State.Qty - qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

State.Qty -= qty;

return State.WriteStateAsync();}

Different levels of abstraction

public Task CheckOut(int qty){

if (qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (State.Qty - qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

State.Qty -= qty;

return State.WriteStateAsync();}

domain

Different levels of abstraction

public Task CheckOut(int qty){

if (qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (State.Qty - qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

State.Qty -= qty;

return State.WriteStateAsync();}

domain

infrastructure

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public override Task OnActivateAsync(){

item = new InventoryItem(GetPrimaryKey(), State);

return base.OnActivateAsync();}

public Task CheckOut(int qty){

item.CheckOut(qty);

return State.WriteStateAsync();}

public Task Deactivate(){

item.Deactivate();

return State.WriteStateAsync();}

class InventoryItem{

readonly string id;readonly IInventoryItemGrainState state;

public InventoryItem(string id, IInventoryItemGrainState state){

this.state = state;this.id = id;

}

public void CheckOut(int qty){

if (qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (state.Qty - qty < 0)throw new InvalidOperationException(

"Can't check-out more than there is in stock");

state.Qty -= qty;}

public void Deactivate(){

if (!state.Active)throw new InvalidOperationException(id + " is inactive");

state.Active = false;}

Framework (infrastructure) dependent

hard to testInfrastructure independent, POCO

easy to test

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public override Task OnActivateAsync(){

item = new InventoryItem(GetPrimaryKey(), State);

return base.OnActivateAsync();}

public Task CheckOut(int qty){

item.CheckOut(qty);

return State.WriteStateAsync();}

public Task Deactivate(){

item.Deactivate();

return State.WriteStateAsync();}

class InventoryItem{

readonly string id;readonly IInventoryItemGrainState state;

public InventoryItem(string id, IInventoryItemGrainState state){

this.state = state;this.id = id;

}

public void CheckOut(int qty){

if (qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (state.Qty - qty < 0)throw new InvalidOperationException(

"Can't check-out more than there is in stock");

state.Qty -= qty;}

public void Deactivate(){

if (!state.Active)throw new InvalidOperationException(id + " is inactive");

state.Active = false;}

Framework (infrastructure) dependent

hard to testInfrastructure independent, POCO

easy to test

[TestFixture]public class InventoryItemFixture{

[Test]public void When_checking_out_more_than_there_is_in_stock(){

var state = new InventoryItemStateStub {Qty = 0};var item = new InventoryItem(state, "test-123"); Assert.Throws<InvalidOperationException>(()=> grain.CheckOut(10));

} }

Unit testing business logic (with POCO)

[TestFixture]public class InventoryItemFixture{

[Test]public void When_checking_out_more_than_there_is_in_stock(){

var state = new InventoryItemStateStub {Qty = 0};var item = new InventoryItem(state, "test-123"); Assert.Throws<InvalidOperationException>(()=> grain.CheckOut(10));

} }

Unit testing business logic (with POCO)

------ Test started: Assembly: Example.Meetup.exe ------

1 passed, 0 failed, 0 skipped, took 0.006 seconds (NUnit 2.6.2).

[TestFixture]public class InventoryItemFixture{

[Test]public void When_checking_out_more_than_there_is_in_stock(){

var state = new InventoryItemStateStub {Qty = 0};var item = new InventoryItem(state, "test-123"); Assert.Throws<InvalidOperationException>(()=> grain.CheckOut(10));

} }

Unit testing business logic (with POCO)

------ Test started: Assembly: Example.Meetup.exe ------

1 passed, 0 failed, 0 skipped, took 0.006 seconds (NUnit 2.6.2).

IS A BREATHE !!!

Duplicationall over the place

interface IInventoryItemGrain : IGrain{

Task CheckOut(int qty);Task Deactivate();…

}

Duplicationall over the place

interface IInventoryItemGrain : IGrain{

Task CheckOut(int qty);Task Deactivate();…

}

class InventoryItemGrain : IInventoryItemGrain{

public Task CheckOut(int qty){

poco.CheckOut(qty);…

}

public Task Deactivate(){

poco.Deactivate();…

}

Duplicationall over the place

interface IInventoryItemGrain : IGrain{

Task CheckOut(int qty);Task Deactivate();…

}

class InventoryItemGrain : IInventoryItemGrain{

public Task CheckOut(int qty){

poco.CheckOut(qty);…

}

public Task Deactivate(){

poco.Deactivate();…

}

class InventoryItem{

public void CheckOut(int qty){

…}

public void Deactivate(){

…}

Duplicationall over the place

interface IInventoryItemGrain : IGrain{

Task CheckOut(int qty);Task Deactivate();…

}

class InventoryItemGrain : IInventoryItemGrain{

public Task CheckOut(int qty){

poco.CheckOut(qty);…

}

public Task Deactivate(){

poco.Deactivate();…

}

class InventoryItem{

public void CheckOut(int qty){

…}

public void Deactivate(){

…}

Duplicationall over the place

interface IInventoryItemGrain : IGrain{

Task CheckOut(int qty);Task Deactivate();…

}

class InventoryItemGrain : IInventoryItemGrain{

public Task CheckOut(int qty){

poco.CheckOut(qty);…

}

public Task Deactivate(){

poco.Deactivate();…

}

class InventoryItem{

public void CheckOut(int qty){

…}

public void Deactivate(){

…}

Signatures are repeated

4 times for every op

Duplicationall over the place

interface IInventoryItemGrain : IGrain{

Task CheckOut(int qty);Task Deactivate();…

}

class InventoryItemGrain : IInventoryItemGrain{

public Task CheckOut(int qty){

poco.CheckOut(qty);…

}

public Task Deactivate(){

poco.Deactivate();…

}

class InventoryItem{

public void CheckOut(int qty){

…}

public void Deactivate(){

…}

This is not DRY!!!

How can we get rid of duplication?Why do we have it?

Non-uniform interfaceis the issue

“uniform” WAT!?!

class InventoryItemGrain : IInventoryItemGrain{

public Task CheckOut(int qty){

poco.CheckOut(qty);…

}

public Task Deactivate(){

poco.Deactivate();…

}

class InventoryItem{

public void CheckOut(int qty){

…}

public void Deactivate(){

…}

Generic dispatch

Non-uniform interface

class InventoryItem{

public void CheckOut(int qty){

…}

public void Deactivate(){

…}

1. Variable method names

2. Variable method signatures

Variable signatures

Introduce Parameter Object

solution

Refactoring, M.Fowler

Introduce Parameter Object

class InventoryItem{

public void CheckOut(int qty){

…}

public void Deactivate(){

…}

public class CheckOutArgs{

public int Qty;}

public class DeactivateArgs{}

class InventoryItem{

public void CheckOut(CheckOutArgs args){

…}

public void Deactivate(DeactivateArgs args){

…}

Parameter Objects(DTO)

class InventoryItemGrain : IInventoryItemGrain{

public Task CheckOut(int qty){

poco.CheckOut(new CheckOutArgs {Qty = qty});…

}

public Task Deactivate(){

poco.Deactivate(new DeactivateArgs());…

}

class InventoryItem{

public void CheckOut(CheckOutArgs args){

…}

public void Deactivate(DeactivateArgs args){

…}

Generic dispatch

class InventoryItemGrain : IInventoryItemGrain{

public Task CheckOut(int qty){

poco.CheckOut(new CheckOutArgs {Qty = qty});…

}

public Task Deactivate(){

poco.Deactivate(new DeactivateArgs());…

}

class InventoryItem{

public void CheckOut(CheckOutArgs args){

…}

public void Deactivate(DeactivateArgs args){

…}

Still unable to make it generic

Generic dispatch

Variable method names

C# overloads

solution

Use overloads

class InventoryItem{

public void Handle(CheckOutArgs args){

…}

public void Handle(DeactivateArgs args){

…}

class InventoryItem{

public void CheckOut(CheckOutArgs args){

…}

public void Deactivate(DeactivateArgs args){

…}

class InventoryItemGrain : IInventoryItemGrain{

public Task CheckOut(int qty){

poco.Handle(new CheckOutArgs {Qty = qty});…

}

public Task Deactivate(){

poco.Handle(new DeactivateArgs());…

}

class InventoryItem{

public void Handle(CheckOutArgs args){

…}

public void Handle(DeactivateArgs args){

…}

Generic dispatch

class InventoryItemGrain : IInventoryItemGrain{

public Task CheckOut(int qty){

poco.Handle(new CheckOutArgs {Qty = qty});…

}

public Task Deactivate(){

poco.Handle(new DeactivateArgs());…

}

class InventoryItem{

public void Handle(CheckOutArgs args){

…}

public void Handle(DeactivateArgs args){

…}

Now we can make it generic

Generic dispatch

class InventoryItem{

public void Handle(CheckOutArgs args){

…}

public void Handle(DeactivateArgs args){

…}

Generic dispatch

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task CheckOut(int qty){

Handle(new CheckOutArgs {Qty = qty}); …

}

public Task Deactivate(){

Handle(new DeactivateArgs());…

}

void Handle(object args){

item.Handle((dynamic)args);}

class InventoryItem{

public void Handle(CheckOutArgs args){

…}

public void Handle(DeactivateArgs args){

…}

Dynamic dispatch Is easy!

Generic dispatch

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task CheckOut(int qty){

Handle(new CheckOutArgs {Qty = qty}); …

}

public Task Deactivate(){

Handle(new DeactivateArgs());…

}

void Handle(object args){

item.Handle((dynamic)args);}

class InventoryItem{

public void Handle(CheckOutArgs args){

…}

public void Handle(DeactivateArgs args){

…}

Non-uniform to uniform

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task CheckOut(int qty){

Handle(new CheckOutArgs {Qty = qty}); …

}

public Task Deactivate(){

Handle(new DeactivateArgs());…

}

void Handle(object args){

item.Handle((dynamic)args);}

class InventoryItem{

public void Handle(CheckOutArgs args){

…}

public void Handle(DeactivateArgs args){

…}

Non-uniform to uniform

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task CheckOut(int qty){

Handle(new CheckOutArgs {Qty = qty}); …

}

public Task Deactivate(){

Handle(new DeactivateArgs());…

}

void Handle(object args){

item.Handle((dynamic)args);}

That’s silly

Uniform grain interface

public interface IInventoryItemGrain : IGrain{

Task CheckOut(int qty);Task Deactivate();…

}

Uniform grain interface

public interface IInventoryItemGrain : IGrain{

Task CheckOut(int qty);Task Deactivate();…

}

public interface IInventoryItemGrain : IGrain{

Task CheckOut(int qty);Task Deactivate();Task Receive(object args);

}

Uniform grain implementation

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task CheckOut(int qty){

Handle(new CheckOutArgs {Qty = qty}); return State.WriteStateAsync();

}

public Task Deactivate(){

Handle(new DeactivateArgs());return State.WriteStateAsync();

}

void Handle(object args){

item.Handle((dynamic)args);}

Uniform grain implementation

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task CheckOut(int qty){

Handle(new CheckOutArgs {Qty = qty}); return State.WriteStateAsync();

}

public Task Deactivate(){

Handle(new DeactivateArgs());return State.WriteStateAsync();

}

void Handle(object args){

item.Handle((dynamic)args);}

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task Receive(object args){

item.Handle((dynamic)args);return State.WriteStateAsync();

}

Uniform grain implementation

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task CheckOut(int qty){

Handle(new CheckOutArgs {Qty = qty}); return State.WriteStateAsync();

}

public Task Deactivate(){

Handle(new DeactivateArgs());return State.WriteStateAsync();

}

void Handle(object args){

item.Handle((dynamic)args);}

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task Receive(object args){

item.Handle((dynamic)args);return State.WriteStateAsync();

}

That’s nice

Grain interface project

public class CheckOutArgs{

public int Qty;}

public class DeactivateArgs{}

Grain interface project

[Serializable]public class CheckOutArgs{

public int Qty;}

[Serializable]public class DeactivateArgs{}

Grain interface project

[Serializable]public class CheckOutArgs{

public int Qty;}

[Serializable]public class DeactivateArgs{}

Grain interface project

[Serializable]public class CheckOutArgs{

public int Qty;}

[Serializable]public class DeactivateArgs{}

Noise

POCO interface

class InventoryItem{

public void Handle(CheckOutArgs args){

…}

public void Handle(DeactivateArgs args){

…}

POCO interface

class InventoryItem{

public void Handle(CheckOut args){

…}

public void Handle(Deactivate args){

…}

POCO interface

class InventoryItem{

public void Handle(CheckOut msg){

…}

public void Handle(Deactivate msg){

…}

Client-side

await grain.CheckOut(15);

was

Client-side

await grain.CheckOut(15);

await grain.Receive(new CheckOut(15));

was

now

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task Receive(object args){

item.Handle((dynamic)args);return State.WriteStateAsync();

}

class InventoryItem{

public void Handle(CheckOut msg){

…}

public void Handle(Deactivate msg){

…}

Grain is a POCO

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task Receive(object args){

item.Handle((dynamic)args);return State.WriteStateAsync();

}

class InventoryItem{

public void Handle(CheckOut msg){

…}

public void Handle(Deactivate msg){

…}

Grain is a POCO

We can merge POCO methods

back to grain(while still keeping them easily testable)

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task Receive(object args){

this.Handle((dynamic)args);return State.WriteStateAsync();

}

public void Handle(CheckOut msg){

…}

public void Handle(Deactivate msg){

…}

Grain is also a POCO

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task Receive(object args){

this.Handle((dynamic)args);return State.WriteStateAsync();

}

public void Handle(CheckOut msg){

…}

public void Handle(Deactivate msg){

…}

Grain is also a POCO

run-time

class InventoryItemGrain : IInventoryItemGrain{

InventoryItem item;

public Task Receive(object args){

this.Handle((dynamic)args);return State.WriteStateAsync();

}

public void Handle(CheckOut msg){

…}

public void Handle(Deactivate msg){

…}

Grain is also a POCO

run-time

unit-tests

[TestFixture]public class InventoryItemFixture{

[Test]public void When_checking_out_more_than_there_is_in_stock(){

var item = new InventoryItem(); Assert.Throws<InvalidOperationException>(()=> grain.Handle(new CheckOut(15)));

} }

Unit testing business logic (with POCO grain)

[TestFixture]public class InventoryItemFixture{

[Test]public void When_checking_out_more_than_there_is_in_stock(){

var item = new InventoryItem(); Assert.Throws<InvalidOperationException>(()=> grain.Handle(new CheckOut(15)));

} }

Unit testing business logic (with POCO grain)

------ Test started: Assembly: Example.Meetup.exe ------

Test 'Example.InventoryItemFixture.When_checking_out_more_than_there_is_in_stock' failed: Expected: <System.InvalidOperationException>But was: <System.NullReferenceException> (Object reference not set to an instance of an object.)at Example.InventoryItemGrain.Handle(CheckOut msg) in C:\Work\Source\Example.Meetup\Fixture.cs:line 44at NUnit.Framework.Assert.Throws(IResolveConstraint expression, TestDelegate code, String message,…)

Fixture.cs(110,0): at Example.InventoryItemFixture.When_checking_out_more_than_there_is_in_stock()

0 passed, 1 failed, 0 skipped, took 0.1 seconds (NUnit 2.6.2).

Inconvinient declarative persistence

public void Handle(CheckOut msg){

if (msg.Qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (State.Qty – msg.Qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

State.Qty -= msg.Qty;}

Inconvinient declarative persistence

public Task Receive(object args){

this.Handle((dynamic)args);

return State.WriteStateAsync();}

public void Handle(CheckOut msg){

if (msg.Qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (State.Qty – msg.Qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

State.Qty -= msg.Qty;}

public void Handle(Deactivate msg){

if (!State.Active)throw new InvalidOperationException(“Item is deactivated");

State.Active = false;}

Inconvinient declarative persistence

public Task Receive(object args){

this.Handle((dynamic)args);

return State.WriteStateAsync();}

public void Handle(CheckOut msg){

if (msg.Qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (State.Qty – msg.Qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

State.Qty -= msg.Qty;}

public void Handle(Deactivate msg){

if (!State.Active)throw new InvalidOperationException(“Item is deactivated");

State.Active = false;}

Separation of Concerns

Separation of Concerns

VIOLATION

OF

IGrainState concept

has mixed levels of abstraction

It is both:

1. Bag of custom state properties

2. And also a Unit of Work

Splitting concerns

class InventoryItemGrain : Grain<IInventoryItemGrainState>, IInventoryItemGrain{

string name;int qty;bool active;

public void Handle(CheckOut msg){

if (msg.Qty <= 0)throw new InvalidOperationException(

"Can't check-out negative quantity from inventory");

if (this.qty – msg.Qty < 0)throw new InvalidOperationException(

"Can't check-out more quantity than there is in stock");

this.qty -= msg.Qty;}

public void Handle(Deactivate msg){

if (!this.active)throw new InvalidOperationException(“Item is deactivated");

this.active = false;}

class InventoryItemGrain : Grain<IInventoryItemGrainState>, IInventoryItemGrain{

string name;int qty;bool active;

public Task Receive(object args){

this.Handle((dynamic)args);

State.Name = this.name;State.Qty = this.qty;State.Active = this.active;

return State.WriteStateAsync();}

Splitting concerns

class InventoryItemGrain : Grain, IInventoryItemGrain{

InventoryItemRepository repo;string name;int qty;bool active;

public Task Receive(object args){

this.Handle((dynamic)args);return repo.StoreAsync(this);

}…

Custom storage repository

Recap

Recap

public interface IInventoryItemGrain : IGrain{

Task Receive(object msg);}

Recap

[Serializable]public class CheckOut{

public int Qty;}

[Serializable]public class Deactivate{}

public interface IInventoryItemGrain : IGrain{

Task Receive(object msg);}

Recap

[Serializable]public class CheckOut{

public int Qty;}

[Serializable]public class Deactivate{}

public interface IInventoryItemGrain : IGrain{

Task Receive(object msg);}

public class InventoryItemGrain : Grain, IInventoryItemGrain{

public Task Receive(object msg){

this.Handle((dynamic)msg);…

}

public void Handle(CheckOut msg){

…}

public void Handle(Deactivate msg){

…}

}

Message Passing

In computer science, message passing sends a message

to a process (which may be an actor or object) and

relies on the process and the supporting infrastructure to

select and invoke the actual code to run. Message

passing differs from conventional programming where a

process, subroutine, or function is directly invoked by

name

From Wikipedia:

Message passing

[Serializable]public class CheckOut{

public int Qty;}

[Serializable]public class Deactivate{}

public interface IInventoryItemGrain : IGrain{

Task Receive(object msg);}

public class InventoryItemGrain : Grain, IInventoryItemGrain{

public Task Receive(object msg){

this.Handle((dynamic)msg);…

}

public void Handle(CheckOut msg){

…}

public void Handle(Deactivate msg){

…}

}

messages

select&invoke(dispatcher)

Functional style

public class InventoryItemGrain : Grain, IInventoryItemGrain{

public Task Receive(object msg){

this.Handle((dynamic)msg);…

}

public void Handle(CheckOut msg){

…}

public void Handle(Deactivate msg){

…}

}

a.k.a.

Higher-order function

Higher-order functions

Messages

+

=

l < 3

Higher-order functions

Messages

+

=

l < 3

Higher-order functions

Messages

+

=

42

Messages

1. The set of messages that a server (grain)

can process defines its public interface

[Serializable]public class CheckOut{

public int Qty;}

[Serializable]public class Deactivate{}

public interface IGrain{

Task<object> Receive(object msg);}

Uniform interface

2. Message is schema

namespace Example

struct CheckOut{

0: int Qty;1: …;

}

struct Deactivate{

0: int Qty;1: …;

}

[Serializable]public class CheckOut{

public int Qty;}

[Serializable]public class Deactivate{}

It all boils down just to serialization

Microsoft Bond Microsoft .NET

Custom protocol is breathe (HTTP)

POST http://host/api/item/123/checkout

CONTENT-TYPE: application/json

BODY:{

qty: 15

}

POST http://host/api/item/123/checkout

CONTENT-TYPE: application/json

BODY:{

qty: 15

}

type id op

Custom protocol is breathe (HTTP)

Higher-order functions

AOP for free

public class InventoryItemGrain : Grain, IInventoryItemGrain{

public Task Receive(object msg){

this.Handle((dynamic)msg);

}}

<pre>

<post>

The most important hook!

the

END