diff --git a/src/mediator/Mediator.Step0/ILoggingService.cs b/src/mediator/Mediator.Step0/ILoggingService.cs new file mode 100644 index 0000000..7ce9add --- /dev/null +++ b/src/mediator/Mediator.Step0/ILoggingService.cs @@ -0,0 +1,6 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +public interface ILoggingService +{ + void Log(string message ); +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step0/IMetricService.cs b/src/mediator/Mediator.Step0/IMetricService.cs new file mode 100644 index 0000000..a09da00 --- /dev/null +++ b/src/mediator/Mediator.Step0/IMetricService.cs @@ -0,0 +1,12 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IMetricService +{ + void ItemDistributed( IWarehouse warehouse, Item item, IStore store ); + void ItemReceived( IStore store, Item item ); + void ItemReturned( IStore store, Item item, IWarehouse warehouse ); + + void Print(); +} diff --git a/src/mediator/Mediator.Step0/IStore.cs b/src/mediator/Mediator.Step0/IStore.cs new file mode 100644 index 0000000..75437a3 --- /dev/null +++ b/src/mediator/Mediator.Step0/IStore.cs @@ -0,0 +1,14 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IStore +{ + string Name { get; } + + IReadOnlyList Items { get; } + + void ReceiveItem(Item item); + + void ReturnItem(Item item); +} diff --git a/src/mediator/Mediator.Step0/IWarehouse.cs b/src/mediator/Mediator.Step0/IWarehouse.cs new file mode 100644 index 0000000..f35b2bc --- /dev/null +++ b/src/mediator/Mediator.Step0/IWarehouse.cs @@ -0,0 +1,12 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IWarehouse +{ + void AddStore(IStore store ); + + void DistributeItem(Item item); + + void AcceptReturnedItem(Item item, IStore store); +} diff --git a/src/mediator/Mediator.Step0/Item.cs b/src/mediator/Mediator.Step0/Item.cs new file mode 100644 index 0000000..1213871 --- /dev/null +++ b/src/mediator/Mediator.Step0/Item.cs @@ -0,0 +1,8 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public class Item +{ + public string? Kind { get; init; } +} diff --git a/src/mediator/Mediator.Step0/LoggingService.cs b/src/mediator/Mediator.Step0/LoggingService.cs new file mode 100644 index 0000000..68c2cc1 --- /dev/null +++ b/src/mediator/Mediator.Step0/LoggingService.cs @@ -0,0 +1,9 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +internal class LoggingService : ILoggingService +{ + public void Log(string message) + { + Console.WriteLine(message); + } +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step0/Mediator.Step0.csproj b/src/mediator/Mediator.Step0/Mediator.Step0.csproj new file mode 100644 index 0000000..7fb4463 --- /dev/null +++ b/src/mediator/Mediator.Step0/Mediator.Step0.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + Mediator + + + + + + + diff --git a/src/mediator/Mediator.Step0/MetricService.cs b/src/mediator/Mediator.Step0/MetricService.cs new file mode 100644 index 0000000..4da4f28 --- /dev/null +++ b/src/mediator/Mediator.Step0/MetricService.cs @@ -0,0 +1,30 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +internal class MetricService : IMetricService +{ + private int _distributedItems; + private int _returnedItems; + private int _receivedItems; + + public void ItemDistributed( IWarehouse warehouse, Item item, IStore store ) + { + this._distributedItems++; + } + + public void ItemReceived( IStore store, Item item ) + { + this._receivedItems++; + } + + public void ItemReturned( IStore store, Item item, IWarehouse warehouse ) + { + this._returnedItems++; + } + + public void Print() + { + Console.WriteLine( $"Distributed: {this._distributedItems}, Received: {this._receivedItems}, Returned: {this._returnedItems}" ); + } +} diff --git a/src/mediator/Mediator.Step0/Program.cs b/src/mediator/Mediator.Step0/Program.cs new file mode 100644 index 0000000..7b424d0 --- /dev/null +++ b/src/mediator/Mediator.Step0/Program.cs @@ -0,0 +1,33 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +using Mediator; +using Microsoft.Extensions.DependencyInjection; + +// Initialize services. +var services = new ServiceCollection(); + +services.AddSingleton(); +services.AddSingleton(); +services.AddSingleton(); + +var serviceProvider = services.BuildServiceProvider(); + +// Create instances of the classes. +var w = serviceProvider.GetRequiredService(); +var s1 = ActivatorUtilities.CreateInstance( serviceProvider, "store1" ); +var s2 = ActivatorUtilities.CreateInstance( serviceProvider, "store2" ); + +var item1 = new Item { Kind = "item1" }; +var item2 = new Item { Kind = "item2" }; + +// Add stores. +w.AddStore( s1 ); +w.AddStore( s2 ); + +// Run the scenario. +w.DistributeItem( item1 ); +w.DistributeItem( item2 ); +s1.ReturnItem( s1.Items[0] ); + +// Print metrics. +serviceProvider.GetRequiredService().Print(); \ No newline at end of file diff --git a/src/mediator/Mediator.Step0/Store.cs b/src/mediator/Mediator.Step0/Store.cs new file mode 100644 index 0000000..3c85c55 --- /dev/null +++ b/src/mediator/Mediator.Step0/Store.cs @@ -0,0 +1,37 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +internal class Store : IStore +{ + private readonly IWarehouse _warehouse; + private readonly IMetricService _metricService; + private readonly ILoggingService _loggingService; + private readonly List _items; + + public string Name { get; } + public IReadOnlyList Items => this._items; + + public Store(string name, IWarehouse warehouse, IMetricService metricService, ILoggingService loggingService ) + { + this.Name = name; + this._warehouse = warehouse; + this._metricService = metricService; + this._loggingService = loggingService; + this._items = new List(); + } + + public void ReceiveItem(Item item) + { + this._items.Add( item ); + this._metricService.ItemReceived( this, item ); + this._loggingService.Log( $"{this.Name} received {item.Kind}." ); + } + + public void ReturnItem(Item item) + { + this._items.Remove( item ); + this._loggingService.Log( $"{this.Name} returned {item.Kind}." ); + this._warehouse.AcceptReturnedItem( item, this ); + } +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step0/Warehouse.cs b/src/mediator/Mediator.Step0/Warehouse.cs new file mode 100644 index 0000000..07fd9f5 --- /dev/null +++ b/src/mediator/Mediator.Step0/Warehouse.cs @@ -0,0 +1,49 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +internal class Warehouse : IWarehouse +{ + private int _nextStore; + private readonly List _stores; + private readonly IMetricService _metricService; + private readonly ILoggingService _loggingService; + + public Warehouse( IMetricService metricService, ILoggingService loggingService ) + { + this._stores = new List(); + this._metricService = metricService; + this._loggingService = loggingService; + } + + public void DistributeItem( Item item ) + { + var store = this._stores[this._nextStore]; + this._loggingService.Log( $"Distributed {item.Kind} to {store.Name}." ); + store.ReceiveItem(item); + this._metricService.ItemDistributed( this, item, store ); + this.ToNextStore(); + } + + public void AddStore( IStore store ) + { + this._stores.Add( store ); + } + + public void AcceptReturnedItem( Item item, IStore store ) + { + this._metricService.ItemReturned( store, item, this ); + + if ( this._stores[this._nextStore] == store) + { + this.ToNextStore(); + } + + this.DistributeItem( item ); + } + + private void ToNextStore() + { + this._nextStore = (this._nextStore + 1) % this._stores.Count; + } +} diff --git a/src/mediator/Mediator.Step1/DistributionMediator.cs b/src/mediator/Mediator.Step1/DistributionMediator.cs new file mode 100644 index 0000000..c218f90 --- /dev/null +++ b/src/mediator/Mediator.Step1/DistributionMediator.cs @@ -0,0 +1,50 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public class DistributionMediator : IDistributionMediator +{ + private int _nextStore; + private readonly Lazy _warehouse; + private readonly List _stores = new(); + private readonly IMetricService _metricService; + private readonly ILoggingService _loggingService; + + public DistributionMediator( Lazy warehouse, IMetricService metricService, ILoggingService loggingService ) + { + this._warehouse = warehouse; + this._metricService = metricService; + this._loggingService = loggingService; + } + + public void Distribute( Item item ) + { + var store = this._stores[this._nextStore]; + this._metricService.ItemDistributed( this._warehouse.Value, item, store ); + this._loggingService.Log( $"Distributed {item.Kind} to {store.Name}." ); + store.ReceiveItem( item ); + this._loggingService.Log( $"{store.Name} received {item.Kind}." ); + this._metricService.ItemReceived( store, item ); + this.ToNextStore(); + } + + public void AddStore( IStore store ) + { + this._stores.Add( store ); + } + + public void Redistribute( Item item, IStore store ) + { + if ( this._stores[this._nextStore] == store ) + { + this.ToNextStore(); + } + + this.Distribute( item ); + } + + private void ToNextStore() + { + this._nextStore = (this._nextStore + 1) % this._stores.Count; + } +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step1/IDistributionMediator.cs b/src/mediator/Mediator.Step1/IDistributionMediator.cs new file mode 100644 index 0000000..bbef4fb --- /dev/null +++ b/src/mediator/Mediator.Step1/IDistributionMediator.cs @@ -0,0 +1,10 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IDistributionMediator +{ + void Distribute( Item item ); + void AddStore( IStore store ); + void Redistribute( Item item, IStore store ); +} diff --git a/src/mediator/Mediator.Step1/ILoggingService.cs b/src/mediator/Mediator.Step1/ILoggingService.cs new file mode 100644 index 0000000..7ce9add --- /dev/null +++ b/src/mediator/Mediator.Step1/ILoggingService.cs @@ -0,0 +1,6 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +public interface ILoggingService +{ + void Log(string message ); +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step1/IMetricService.cs b/src/mediator/Mediator.Step1/IMetricService.cs new file mode 100644 index 0000000..a09da00 --- /dev/null +++ b/src/mediator/Mediator.Step1/IMetricService.cs @@ -0,0 +1,12 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IMetricService +{ + void ItemDistributed( IWarehouse warehouse, Item item, IStore store ); + void ItemReceived( IStore store, Item item ); + void ItemReturned( IStore store, Item item, IWarehouse warehouse ); + + void Print(); +} diff --git a/src/mediator/Mediator.Step1/IReturnMediator.cs b/src/mediator/Mediator.Step1/IReturnMediator.cs new file mode 100644 index 0000000..b8e8a1e --- /dev/null +++ b/src/mediator/Mediator.Step1/IReturnMediator.cs @@ -0,0 +1,8 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IReturnMediator +{ + void ReturnItem( Item item, IStore store ); +} diff --git a/src/mediator/Mediator.Step1/IStore.cs b/src/mediator/Mediator.Step1/IStore.cs new file mode 100644 index 0000000..75437a3 --- /dev/null +++ b/src/mediator/Mediator.Step1/IStore.cs @@ -0,0 +1,14 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IStore +{ + string Name { get; } + + IReadOnlyList Items { get; } + + void ReceiveItem(Item item); + + void ReturnItem(Item item); +} diff --git a/src/mediator/Mediator.Step1/IWarehouse.cs b/src/mediator/Mediator.Step1/IWarehouse.cs new file mode 100644 index 0000000..8d70f23 --- /dev/null +++ b/src/mediator/Mediator.Step1/IWarehouse.cs @@ -0,0 +1,10 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IWarehouse +{ + void DistributeItem(Item item); + + void AcceptReturnedItem(Item item, IStore store); +} diff --git a/src/mediator/Mediator.Step1/Item.cs b/src/mediator/Mediator.Step1/Item.cs new file mode 100644 index 0000000..1213871 --- /dev/null +++ b/src/mediator/Mediator.Step1/Item.cs @@ -0,0 +1,8 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public class Item +{ + public string? Kind { get; init; } +} diff --git a/src/mediator/Mediator.Step1/LoggingService.cs b/src/mediator/Mediator.Step1/LoggingService.cs new file mode 100644 index 0000000..68c2cc1 --- /dev/null +++ b/src/mediator/Mediator.Step1/LoggingService.cs @@ -0,0 +1,9 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +internal class LoggingService : ILoggingService +{ + public void Log(string message) + { + Console.WriteLine(message); + } +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step1/Mediator.Step1.csproj b/src/mediator/Mediator.Step1/Mediator.Step1.csproj new file mode 100644 index 0000000..7fb4463 --- /dev/null +++ b/src/mediator/Mediator.Step1/Mediator.Step1.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + Mediator + + + + + + + diff --git a/src/mediator/Mediator.Step1/MetricService.cs b/src/mediator/Mediator.Step1/MetricService.cs new file mode 100644 index 0000000..4da4f28 --- /dev/null +++ b/src/mediator/Mediator.Step1/MetricService.cs @@ -0,0 +1,30 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +internal class MetricService : IMetricService +{ + private int _distributedItems; + private int _returnedItems; + private int _receivedItems; + + public void ItemDistributed( IWarehouse warehouse, Item item, IStore store ) + { + this._distributedItems++; + } + + public void ItemReceived( IStore store, Item item ) + { + this._receivedItems++; + } + + public void ItemReturned( IStore store, Item item, IWarehouse warehouse ) + { + this._returnedItems++; + } + + public void Print() + { + Console.WriteLine( $"Distributed: {this._distributedItems}, Received: {this._receivedItems}, Returned: {this._returnedItems}" ); + } +} diff --git a/src/mediator/Mediator.Step1/Program.cs b/src/mediator/Mediator.Step1/Program.cs new file mode 100644 index 0000000..dfd204d --- /dev/null +++ b/src/mediator/Mediator.Step1/Program.cs @@ -0,0 +1,37 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +using Mediator; +using Microsoft.Extensions.DependencyInjection; + +// Initialize services. +var services = new ServiceCollection(); + +services.AddTransient( sp => new Lazy( sp.GetRequiredService ) ); +services.AddSingleton(); +services.AddSingleton(); +services.AddSingleton(); +services.AddSingleton(); +services.AddSingleton(); + +var serviceProvider = services.BuildServiceProvider(); + +// Create instances of the classes. +var w = serviceProvider.GetRequiredService(); +var distributionMediator = serviceProvider.GetRequiredService(); +var s1 = ActivatorUtilities.CreateInstance( serviceProvider, "store1" ); +var s2 = ActivatorUtilities.CreateInstance( serviceProvider, "store2" ); + +var item1 = new Item { Kind = "item1" }; +var item2 = new Item { Kind = "item2" }; + +// Add stores. +distributionMediator.AddStore( s1 ); +distributionMediator.AddStore( s2 ); + +// Run the scenario. +w.DistributeItem( item1 ); +w.DistributeItem( item2 ); +s1.ReturnItem( s1.Items[0] ); + +// Print metrics. +serviceProvider.GetRequiredService().Print(); \ No newline at end of file diff --git a/src/mediator/Mediator.Step1/ReturnMediator.cs b/src/mediator/Mediator.Step1/ReturnMediator.cs new file mode 100644 index 0000000..847550c --- /dev/null +++ b/src/mediator/Mediator.Step1/ReturnMediator.cs @@ -0,0 +1,24 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public class ReturnMediator: IReturnMediator +{ + private readonly IWarehouse _warehouse; + private readonly IMetricService _metricService; + private readonly ILoggingService _loggingService; + + public ReturnMediator( IWarehouse warehouse, IMetricService metricService, ILoggingService loggingService ) + { + this._warehouse = warehouse; + this._metricService = metricService; + this._loggingService = loggingService; + } + + public void ReturnItem( Item item, IStore store ) + { + this._loggingService.Log( $"{store.Name} returned {item.Kind}." ); + this._metricService.ItemReturned(store, item, this._warehouse); + this._warehouse.AcceptReturnedItem( item, store ); + } +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step1/Store.cs b/src/mediator/Mediator.Step1/Store.cs new file mode 100644 index 0000000..061c785 --- /dev/null +++ b/src/mediator/Mediator.Step1/Store.cs @@ -0,0 +1,31 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public class Store : IStore +{ + private readonly IReturnMediator _returnMediator; + private readonly List _items; + + public string Name { get; } + + public IReadOnlyList Items => this._items; + + public Store(string name, IReturnMediator returnMediator) + { + this.Name = name; + this._returnMediator = returnMediator; + this._items = new List(); + } + + public void ReceiveItem(Item item) + { + this._items.Add( item ); + } + + public void ReturnItem(Item item) + { + this._items.Remove( item ); + this._returnMediator.ReturnItem( item, this ); + } +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step1/Warehouse.cs b/src/mediator/Mediator.Step1/Warehouse.cs new file mode 100644 index 0000000..8de664f --- /dev/null +++ b/src/mediator/Mediator.Step1/Warehouse.cs @@ -0,0 +1,23 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public class Warehouse : IWarehouse +{ + private readonly IDistributionMediator _distributionMediator; + + public Warehouse( IDistributionMediator distributionMediator ) + { + this._distributionMediator = distributionMediator; + } + + public void DistributeItem( Item item ) + { + this._distributionMediator.Distribute( item ); + } + + public void AcceptReturnedItem( Item item, IStore store ) + { + this._distributionMediator.Redistribute( item, store ); + } +} diff --git a/src/mediator/Mediator.Step2/DistributionMediator.cs b/src/mediator/Mediator.Step2/DistributionMediator.cs new file mode 100644 index 0000000..c218f90 --- /dev/null +++ b/src/mediator/Mediator.Step2/DistributionMediator.cs @@ -0,0 +1,50 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public class DistributionMediator : IDistributionMediator +{ + private int _nextStore; + private readonly Lazy _warehouse; + private readonly List _stores = new(); + private readonly IMetricService _metricService; + private readonly ILoggingService _loggingService; + + public DistributionMediator( Lazy warehouse, IMetricService metricService, ILoggingService loggingService ) + { + this._warehouse = warehouse; + this._metricService = metricService; + this._loggingService = loggingService; + } + + public void Distribute( Item item ) + { + var store = this._stores[this._nextStore]; + this._metricService.ItemDistributed( this._warehouse.Value, item, store ); + this._loggingService.Log( $"Distributed {item.Kind} to {store.Name}." ); + store.ReceiveItem( item ); + this._loggingService.Log( $"{store.Name} received {item.Kind}." ); + this._metricService.ItemReceived( store, item ); + this.ToNextStore(); + } + + public void AddStore( IStore store ) + { + this._stores.Add( store ); + } + + public void Redistribute( Item item, IStore store ) + { + if ( this._stores[this._nextStore] == store ) + { + this.ToNextStore(); + } + + this.Distribute( item ); + } + + private void ToNextStore() + { + this._nextStore = (this._nextStore + 1) % this._stores.Count; + } +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step2/IDistributionMediator.cs b/src/mediator/Mediator.Step2/IDistributionMediator.cs new file mode 100644 index 0000000..5d7630e --- /dev/null +++ b/src/mediator/Mediator.Step2/IDistributionMediator.cs @@ -0,0 +1,10 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IDistributionMediator : IMediator +{ + void Distribute( Item item ); + void AddStore( IStore store ); + void Redistribute( Item item, IStore store ); +} diff --git a/src/mediator/Mediator.Step2/ILoggingService.cs b/src/mediator/Mediator.Step2/ILoggingService.cs new file mode 100644 index 0000000..7ce9add --- /dev/null +++ b/src/mediator/Mediator.Step2/ILoggingService.cs @@ -0,0 +1,6 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +public interface ILoggingService +{ + void Log(string message ); +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step2/IMediator.cs b/src/mediator/Mediator.Step2/IMediator.cs new file mode 100644 index 0000000..e5b64c0 --- /dev/null +++ b/src/mediator/Mediator.Step2/IMediator.cs @@ -0,0 +1,8 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator +{ + public interface IMediator + { + } +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step2/IMetricService.cs b/src/mediator/Mediator.Step2/IMetricService.cs new file mode 100644 index 0000000..a09da00 --- /dev/null +++ b/src/mediator/Mediator.Step2/IMetricService.cs @@ -0,0 +1,12 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IMetricService +{ + void ItemDistributed( IWarehouse warehouse, Item item, IStore store ); + void ItemReceived( IStore store, Item item ); + void ItemReturned( IStore store, Item item, IWarehouse warehouse ); + + void Print(); +} diff --git a/src/mediator/Mediator.Step2/IReturnMediator.cs b/src/mediator/Mediator.Step2/IReturnMediator.cs new file mode 100644 index 0000000..fe2f863 --- /dev/null +++ b/src/mediator/Mediator.Step2/IReturnMediator.cs @@ -0,0 +1,8 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IReturnMediator : IMediator +{ + void ReturnItem( Item item, IStore store ); +} diff --git a/src/mediator/Mediator.Step2/IStore.cs b/src/mediator/Mediator.Step2/IStore.cs new file mode 100644 index 0000000..5e60402 --- /dev/null +++ b/src/mediator/Mediator.Step2/IStore.cs @@ -0,0 +1,15 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IStore +{ + string Name { get; } + + IReadOnlyList Items { get; } + + [MediatedBy( typeof( IDistributionMediator ) )] + void ReceiveItem(Item item); + + void ReturnItem(Item item); +} diff --git a/src/mediator/Mediator.Step2/IWarehouse.cs b/src/mediator/Mediator.Step2/IWarehouse.cs new file mode 100644 index 0000000..b9fa7b8 --- /dev/null +++ b/src/mediator/Mediator.Step2/IWarehouse.cs @@ -0,0 +1,11 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public interface IWarehouse +{ + void DistributeItem( Item item ); + + [MediatedBy( typeof( IReturnMediator ) )] + void AcceptReturnedItem( Item item, IStore store ); +} diff --git a/src/mediator/Mediator.Step2/Item.cs b/src/mediator/Mediator.Step2/Item.cs new file mode 100644 index 0000000..1213871 --- /dev/null +++ b/src/mediator/Mediator.Step2/Item.cs @@ -0,0 +1,8 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public class Item +{ + public string? Kind { get; init; } +} diff --git a/src/mediator/Mediator.Step2/LoggingService.cs b/src/mediator/Mediator.Step2/LoggingService.cs new file mode 100644 index 0000000..68c2cc1 --- /dev/null +++ b/src/mediator/Mediator.Step2/LoggingService.cs @@ -0,0 +1,9 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +internal class LoggingService : ILoggingService +{ + public void Log(string message) + { + Console.WriteLine(message); + } +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step2/MediatedByAttribute.cs b/src/mediator/Mediator.Step2/MediatedByAttribute.cs new file mode 100644 index 0000000..75aa581 --- /dev/null +++ b/src/mediator/Mediator.Step2/MediatedByAttribute.cs @@ -0,0 +1,55 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +using Metalama.Extensions.Architecture.Aspects; +using Metalama.Extensions.Architecture.Predicates; +using Metalama.Extensions.Architecture.Validators; +using Metalama.Framework.Aspects; +using Metalama.Framework.Code; +using Metalama.Framework.Eligibility; +using Metalama.Framework.Validation; + +[Inheritable] +public class MediatedByAttribute : BaseUsageValidationAttribute, IAspect +{ + private readonly Type[] _allowedMediatorTypes; + + public MediatedByAttribute( params Type[] allowedMediatorTypes ) + { + this._allowedMediatorTypes = allowedMediatorTypes; + } + + public void BuildEligibility( IEligibilityBuilder builder ) { } + + public void BuildAspect( IAspectBuilder builder ) + { + var predicateBuilder = new ReferencePredicateBuilder( ReferenceEndRole.Origin, builder ); + + builder.Outbound.ValidateOutboundReferences( + new ReferencePredicateValidator( + new HasMediatorAccessPredicate( this._allowedMediatorTypes, predicateBuilder ), + this.Description, + this.ReferenceKinds ) ); + } + internal class HasMediatorAccessPredicate : ReferencePredicate + { + private readonly Type[] _allowedMediatorTypes; + + public HasMediatorAccessPredicate( Type[] allowedMediatorTypes, ReferencePredicateBuilder builder ) + : base( builder ) + { + this._allowedMediatorTypes = allowedMediatorTypes; + } + + public override bool IsMatch( ReferenceValidationContext context ) + { + return context.References.All( + r => + r.ReferencingDeclaration is IMember { IsStatic: true } + || this._allowedMediatorTypes.Any( + amt => r.ReferencingDeclaration.GetTopmostNamedType()!.Is( TypeFactory.GetType( amt ) ) ) ); + } + + public override ReferenceGranularity Granularity => ReferenceGranularity.TopLevelType; + } + +} diff --git a/src/mediator/Mediator.Step2/Mediator.Step2.csproj b/src/mediator/Mediator.Step2/Mediator.Step2.csproj new file mode 100644 index 0000000..1640f99 --- /dev/null +++ b/src/mediator/Mediator.Step2/Mediator.Step2.csproj @@ -0,0 +1,17 @@ + + + + Exe + net6.0 + enable + enable + Mediator + + + + + + + + + diff --git a/src/mediator/Mediator.Step2/MetricService.cs b/src/mediator/Mediator.Step2/MetricService.cs new file mode 100644 index 0000000..4da4f28 --- /dev/null +++ b/src/mediator/Mediator.Step2/MetricService.cs @@ -0,0 +1,30 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +internal class MetricService : IMetricService +{ + private int _distributedItems; + private int _returnedItems; + private int _receivedItems; + + public void ItemDistributed( IWarehouse warehouse, Item item, IStore store ) + { + this._distributedItems++; + } + + public void ItemReceived( IStore store, Item item ) + { + this._receivedItems++; + } + + public void ItemReturned( IStore store, Item item, IWarehouse warehouse ) + { + this._returnedItems++; + } + + public void Print() + { + Console.WriteLine( $"Distributed: {this._distributedItems}, Received: {this._receivedItems}, Returned: {this._returnedItems}" ); + } +} diff --git a/src/mediator/Mediator.Step2/Program.cs b/src/mediator/Mediator.Step2/Program.cs new file mode 100644 index 0000000..402f41a --- /dev/null +++ b/src/mediator/Mediator.Step2/Program.cs @@ -0,0 +1,37 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +using Mediator; +using Microsoft.Extensions.DependencyInjection; + +// Initialize services. +var services = new ServiceCollection(); + +services.AddTransient( sp => new Lazy( sp.GetRequiredService ) ); +services.AddSingleton(); +services.AddSingleton(); +services.AddSingleton(); +services.AddSingleton(); +services.AddSingleton(); + +var serviceProvider = services.BuildServiceProvider(); + +// Create instances of the classes. +var w = serviceProvider.GetRequiredService(); +var distributionMediator = serviceProvider.GetRequiredService(); +var s1 = ActivatorUtilities.CreateInstance( serviceProvider, "store1" ); +var s2 = ActivatorUtilities.CreateInstance( serviceProvider, "store2" ); + +var item1 = new Item { Kind = "item1" }; +var item2 = new Item { Kind = "item2" }; + +// Add stores. +distributionMediator.AddStore( s1 ); +distributionMediator.AddStore( s2 ); + +// Run the scenario. +w.DistributeItem( item1 ); +w.DistributeItem( item2 ); +s1.ReturnItem( s1.Items[0] ); + +// Print metrics. +serviceProvider.GetRequiredService().Print(); diff --git a/src/mediator/Mediator.Step2/ReturnMediator.cs b/src/mediator/Mediator.Step2/ReturnMediator.cs new file mode 100644 index 0000000..0cb4249 --- /dev/null +++ b/src/mediator/Mediator.Step2/ReturnMediator.cs @@ -0,0 +1,24 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public class ReturnMediator : IReturnMediator +{ + private readonly IWarehouse _warehouse; + private readonly IMetricService _metricService; + private readonly ILoggingService _loggingService; + + public ReturnMediator( IWarehouse warehouse, IMetricService metricService, ILoggingService loggingService ) + { + this._warehouse = warehouse; + this._metricService = metricService; + this._loggingService = loggingService; + } + + public void ReturnItem( Item item, IStore store ) + { + this._loggingService.Log( $"{store.Name} returned {item.Kind}." ); + this._metricService.ItemReturned(store, item, this._warehouse); + this._warehouse.AcceptReturnedItem( item, store ); + } +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step2/Store.cs b/src/mediator/Mediator.Step2/Store.cs new file mode 100644 index 0000000..061c785 --- /dev/null +++ b/src/mediator/Mediator.Step2/Store.cs @@ -0,0 +1,31 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public class Store : IStore +{ + private readonly IReturnMediator _returnMediator; + private readonly List _items; + + public string Name { get; } + + public IReadOnlyList Items => this._items; + + public Store(string name, IReturnMediator returnMediator) + { + this.Name = name; + this._returnMediator = returnMediator; + this._items = new List(); + } + + public void ReceiveItem(Item item) + { + this._items.Add( item ); + } + + public void ReturnItem(Item item) + { + this._items.Remove( item ); + this._returnMediator.ReturnItem( item, this ); + } +} \ No newline at end of file diff --git a/src/mediator/Mediator.Step2/Warehouse.cs b/src/mediator/Mediator.Step2/Warehouse.cs new file mode 100644 index 0000000..8de664f --- /dev/null +++ b/src/mediator/Mediator.Step2/Warehouse.cs @@ -0,0 +1,23 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +namespace Mediator; + +public class Warehouse : IWarehouse +{ + private readonly IDistributionMediator _distributionMediator; + + public Warehouse( IDistributionMediator distributionMediator ) + { + this._distributionMediator = distributionMediator; + } + + public void DistributeItem( Item item ) + { + this._distributionMediator.Distribute( item ); + } + + public void AcceptReturnedItem( Item item, IStore store ) + { + this._distributionMediator.Redistribute( item, store ); + } +} diff --git a/src/mediator/Mediator.sln b/src/mediator/Mediator.sln new file mode 100644 index 0000000..e39ffc7 --- /dev/null +++ b/src/mediator/Mediator.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34728.123 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mediator.Step0", "Mediator.Step0\Mediator.Step0.csproj", "{521DCF18-D18B-4EDD-9EDE-E8E61601F895}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mediator.Step1", "Mediator.Step1\Mediator.Step1.csproj", "{359EC5B9-C6D5-413E-BF1A-609C09637654}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mediator.Step2", "Mediator.Step2\Mediator.Step2.csproj", "{F0E5C82C-F3FE-4776-9C63-5B6831D60BD8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + LamaDebug|Any CPU = LamaDebug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {521DCF18-D18B-4EDD-9EDE-E8E61601F895}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {521DCF18-D18B-4EDD-9EDE-E8E61601F895}.Debug|Any CPU.Build.0 = Debug|Any CPU + {521DCF18-D18B-4EDD-9EDE-E8E61601F895}.LamaDebug|Any CPU.ActiveCfg = LamaDebug|Any CPU + {521DCF18-D18B-4EDD-9EDE-E8E61601F895}.LamaDebug|Any CPU.Build.0 = LamaDebug|Any CPU + {521DCF18-D18B-4EDD-9EDE-E8E61601F895}.Release|Any CPU.ActiveCfg = Release|Any CPU + {521DCF18-D18B-4EDD-9EDE-E8E61601F895}.Release|Any CPU.Build.0 = Release|Any CPU + {359EC5B9-C6D5-413E-BF1A-609C09637654}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {359EC5B9-C6D5-413E-BF1A-609C09637654}.Debug|Any CPU.Build.0 = Debug|Any CPU + {359EC5B9-C6D5-413E-BF1A-609C09637654}.LamaDebug|Any CPU.ActiveCfg = LamaDebug|Any CPU + {359EC5B9-C6D5-413E-BF1A-609C09637654}.LamaDebug|Any CPU.Build.0 = LamaDebug|Any CPU + {359EC5B9-C6D5-413E-BF1A-609C09637654}.Release|Any CPU.ActiveCfg = Release|Any CPU + {359EC5B9-C6D5-413E-BF1A-609C09637654}.Release|Any CPU.Build.0 = Release|Any CPU + {F0E5C82C-F3FE-4776-9C63-5B6831D60BD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0E5C82C-F3FE-4776-9C63-5B6831D60BD8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0E5C82C-F3FE-4776-9C63-5B6831D60BD8}.LamaDebug|Any CPU.ActiveCfg = Debug|Any CPU + {F0E5C82C-F3FE-4776-9C63-5B6831D60BD8}.LamaDebug|Any CPU.Build.0 = Debug|Any CPU + {F0E5C82C-F3FE-4776-9C63-5B6831D60BD8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0E5C82C-F3FE-4776-9C63-5B6831D60BD8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {73030782-4264-41EF-9DCA-622D012FAC73} + EndGlobalSection +EndGlobal diff --git a/src/mediator/Mediator/Mediator.csproj b/src/mediator/Mediator/Mediator.csproj new file mode 100644 index 0000000..4fef825 --- /dev/null +++ b/src/mediator/Mediator/Mediator.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + diff --git a/src/mediator/Mediator/Program.cs b/src/mediator/Mediator/Program.cs new file mode 100644 index 0000000..476aa20 --- /dev/null +++ b/src/mediator/Mediator/Program.cs @@ -0,0 +1,146 @@ +// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details. + +using Metalama.Extensions.Architecture.Aspects; +using Metalama.Extensions.Architecture.Predicates; +using Metalama.Extensions.Architecture.Validators; +using Metalama.Framework.Aspects; +using Metalama.Framework.Code; +using Metalama.Framework.Eligibility; +using Metalama.Framework.Validation; + +namespace Mediator; + +internal class Program +{ + static void Main( string[] args ) + { + Warehouse a1 = new Warehouse(); + Store a2 = new Store(); + + a1.DistributeItem(); + a2.ReturnItem(); + } +} + +[ShouldOnlyBeUsedFromMediator( typeof( IWarehouseMediator ) )] +public class Warehouse +{ + private readonly IWarehouseMediator _mediator; + + public void DistributeItem() + { + Console.WriteLine( "Foo" ); + this._mediator.Foo( this ); + } + +#pragma warning disable CA1822 // Mark members as static + public void OnReturnItem() +#pragma warning restore CA1822 // Mark members as static + { + Console.WriteLine( "OnBar" ); + } +} + +[ShouldOnlyBeUsedFromMediator( typeof(IWarehouseMediator) )] +public class Store +{ + private readonly IWarehouseMediator _mediator; + + public void ReturnItem() + { + Console.WriteLine( "Bar" ); + this._mediator.Bar( this ); + } + +#pragma warning disable CA1822 // Mark members as static + public void OnDistributeItem() +#pragma warning restore CA1822 // Mark members as static + { + Console.WriteLine( "OnFoo" ); + } +} + +public class Supplier +{ + private Warehouse a; + + public void ReceiveItem( Item item ) + { + a.DistributeItem(); + } +} + +public class Item +{ +} + +public interface IMediator +{ +} + +public interface IWarehouseMediator : IMediator +{ + void Foo( Warehouse actor1 ); + void Bar( Store actor2); +} + +public class ActorMediator : IWarehouseMediator +{ + private readonly Warehouse _actor1; + private readonly Store _actor2; + + public ActorMediator( Warehouse actor1, Store actor2 ) + { + this._actor1 = actor1; + this._actor2 = actor2; + } + + public void Foo(Warehouse actor1) + { + this._actor2.OnDistributeItem(); + } + + public void Bar( Store actor2 ) + { + this._actor1.OnReturnItem(); + } +} + +public class ShouldOnlyBeUsedFromMediatorAttribute : BaseUsageValidationAttribute, IAspect +{ + private readonly Type[] _allowedMediatorTypes; + + public ShouldOnlyBeUsedFromMediatorAttribute( params Type[] allowedMediatorTypes) + { + this._allowedMediatorTypes = allowedMediatorTypes; + } + + public void BuildEligibility( IEligibilityBuilder builder ) { } + + public void BuildAspect( IAspectBuilder builder ) + { + var predicateBuilder = new ReferencePredicateBuilder( ReferenceEndRole.Origin, builder ); + + builder.Outbound.ValidateOutboundReferences( + new ReferencePredicateValidator( + new HasMediatorAccessPredicate( this._allowedMediatorTypes, predicateBuilder ), + this.Description, + this.ReferenceKinds ) ); + } +} +internal class HasMediatorAccessPredicate : ReferencePredicate +{ + private readonly Type[] _allowedMediatorTypes; + + public HasMediatorAccessPredicate( Type[] allowedMediatorTypes, ReferencePredicateBuilder builder ) : base( builder ) + { + this._allowedMediatorTypes = allowedMediatorTypes; + } + + public override bool IsMatch( ReferenceValidationContext context ) + { + return context.References.All( r => r.ReferencingDeclaration is IMember { IsStatic:true } || this._allowedMediatorTypes.Any( amt => r.ReferencingDeclaration.GetTopmostNamedType().Is( TypeFactory.GetType( amt ) ) ) ); + } + + public override ReferenceGranularity Granularity => ReferenceGranularity.TopLevelType; +} \ No newline at end of file