Upload
yevhen-bobrov
View
34
Download
1
Embed Size (px)
Citation preview
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 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)
Is slow.
Unit testing business logic
Is hard.
Doesn’t worth the price.
Don’t fight the framework.
Just write an integration test.
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!!!
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
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
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
[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){
…}
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;}
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
[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){
…}
}
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
[Serializable]public class CheckOut{
public int Qty;}
[Serializable]public class Deactivate{}
public interface IGrain{
Task<object> Receive(object msg);}
Uniform interface
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)
AOP for free
public class InventoryItemGrain : Grain, IInventoryItemGrain{
public Task Receive(object msg){
this.Handle((dynamic)msg);
}}
<pre>
<post>
The most important hook!