Skip to content

Commit c5e3b90

Browse files
committed
Add Pricing Strategies for Subscriptions (Strategy Pattern)
1 parent 78e3ec5 commit c5e3b90

File tree

13 files changed

+160
-25
lines changed

13 files changed

+160
-25
lines changed

src/Modules/Payments/Application/PriceListItems/GetPriceListItems/GetPriceListItemsQueryHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public GetPriceListItemsQueryHandler(ISqlConnectionFactory sqlConnectionFactory)
1717

1818
public async Task<List<PriceListItemDto>> Handle(GetPriceListItemsQuery request, CancellationToken cancellationToken)
1919
{
20-
return await PriceListProvider.GetPriceListItems(_sqlConnectionFactory.GetOpenConnection());
20+
return await PriceListFactory.GetPriceListItems(_sqlConnectionFactory.GetOpenConnection());
2121
}
2222
}
2323
}

src/Modules/Payments/Application/PriceListItems/PriceListProvider.cs renamed to src/Modules/Payments/Application/PriceListItems/PriceListFactory.cs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,34 @@
33
using System.Linq;
44
using System.Threading.Tasks;
55
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems;
6+
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies;
67
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
78
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
89
using Dapper;
910

1011
namespace CompanyName.MyMeetings.Modules.Payments.Application.PriceListItems
1112
{
12-
public static class PriceListProvider
13+
public static class PriceListFactory
1314
{
14-
public static async Task<PriceList> GetPriceList(IDbConnection connection)
15+
public static async Task<PriceList> CreatePriceList(IDbConnection connection)
1516
{
1617
var priceListItemList = await GetPriceListItems(connection);
1718

18-
return PriceList.CreateFromItems(
19-
priceListItemList
20-
.Select(x =>
21-
new PriceListItemData(
22-
x.CountryCode,
23-
SubscriptionPeriod.Of(x.SubscriptionPeriodCode),
24-
MoneyValue.Of(x.MoneyValue, x.MoneyCurrency),
25-
PriceListItemCategory.Of(x.CategoryCode)))
26-
.ToList());
19+
var priceListItems = priceListItemList
20+
.Select(x =>
21+
new PriceListItemData(
22+
x.CountryCode,
23+
SubscriptionPeriod.Of(x.SubscriptionPeriodCode),
24+
MoneyValue.Of(x.MoneyValue, x.MoneyCurrency),
25+
PriceListItemCategory.Of(x.CategoryCode)))
26+
.ToList();
27+
28+
// This is place for selecting pricing strategy based on provided data and the system state.
29+
IPricingStrategy pricingStrategy = new DirectValueFromPriceListPricingStrategy(priceListItems);
30+
31+
return PriceList.Create(
32+
priceListItems,
33+
pricingStrategy);
2734
}
2835

2936
public static async Task<List<PriceListItemDto>> GetPriceListItems(IDbConnection connection)

src/Modules/Payments/Application/Subscriptions/BuySubscription/BuySubscriptionCommandHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ internal BuySubscriptionCommandHandler(
3131

3232
public async Task<Guid> Handle(BuySubscriptionCommand command, CancellationToken cancellationToken)
3333
{
34-
var priceList = await PriceListProvider.GetPriceList(_sqlConnectionFactory.GetOpenConnection());
34+
var priceList = await PriceListFactory.CreatePriceList(_sqlConnectionFactory.GetOpenConnection());
3535

3636
var subscription = SubscriptionPayment.Buy(
3737
_payerContext.PayerId,

src/Modules/Payments/Application/Subscriptions/BuySubscriptionRenewal/BuySubscriptionRenewalCommandHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ internal BuySubscriptionRenewalCommandHandler(
3636

3737
public async Task<Guid> Handle(BuySubscriptionRenewalCommand command, CancellationToken cancellationToken)
3838
{
39-
var priceList = await PriceListProvider.GetPriceList(_sqlConnectionFactory.GetOpenConnection());
39+
var priceList = await PriceListFactory.CreatePriceList(_sqlConnectionFactory.GetOpenConnection());
4040

4141
var subscriptionId = new SubscriptionId(command.SubscriptionId);
4242

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System.Collections.Generic;
2-
using System.Linq;
32
using CompanyName.MyMeetings.BuildingBlocks.Domain;
3+
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies;
44
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
55
using CompanyName.MyMeetings.Modules.Payments.Domain.SubscriptionPayments.Rules;
66
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
@@ -11,14 +11,21 @@ public class PriceList : ValueObject
1111
{
1212
private readonly List<PriceListItemData> _items;
1313

14-
private PriceList(List<PriceListItemData> items)
14+
private readonly IPricingStrategy _pricingStrategy;
15+
16+
private PriceList(
17+
List<PriceListItemData> items,
18+
IPricingStrategy pricingStrategy)
1519
{
1620
_items = items;
21+
_pricingStrategy = pricingStrategy;
1722
}
1823

19-
public static PriceList CreateFromItems(List<PriceListItemData> items)
24+
public static PriceList Create(
25+
List<PriceListItemData> items,
26+
IPricingStrategy pricingStrategy)
2027
{
21-
return new PriceList(items);
28+
return new PriceList(items, pricingStrategy);
2229
}
2330

2431
public MoneyValue GetPrice(
@@ -28,11 +35,7 @@ public MoneyValue GetPrice(
2835
{
2936
CheckRule(new PriceForSubscriptionMustBeDefinedRule(countryCode, subscriptionPeriod, _items, category));
3037

31-
var priceListItem = _items.Single(x =>
32-
x.CountryCode == countryCode && x.SubscriptionPeriod == subscriptionPeriod &&
33-
x.Category == category);
34-
35-
return priceListItem.Value;
38+
return _pricingStrategy.GetPrice(countryCode, subscriptionPeriod, category);
3639
}
3740
}
3841
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
4+
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
5+
6+
namespace CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies
7+
{
8+
public class DirectValueFromPriceListPricingStrategy : IPricingStrategy
9+
{
10+
private readonly List<PriceListItemData> _items;
11+
12+
public DirectValueFromPriceListPricingStrategy(List<PriceListItemData> items)
13+
{
14+
_items = items;
15+
}
16+
17+
public MoneyValue GetPrice(
18+
string countryCode,
19+
SubscriptionPeriod subscriptionPeriod,
20+
PriceListItemCategory category)
21+
{
22+
var priceListItem = _items.Single(x =>
23+
x.CountryCode == countryCode && x.SubscriptionPeriod == subscriptionPeriod &&
24+
x.Category == category);
25+
26+
return priceListItem.Value;
27+
}
28+
}
29+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
2+
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
3+
4+
namespace CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies
5+
{
6+
public class DirectValuePricingStrategy : IPricingStrategy
7+
{
8+
private readonly MoneyValue _directValue;
9+
10+
public DirectValuePricingStrategy(MoneyValue directValue)
11+
{
12+
_directValue = directValue;
13+
}
14+
15+
public MoneyValue GetPrice(string countryCode, SubscriptionPeriod subscriptionPeriod, PriceListItemCategory category)
16+
{
17+
return _directValue;
18+
}
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
4+
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
5+
6+
namespace CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies
7+
{
8+
public class DiscountedValueFromPriceListPricingStrategy : IPricingStrategy
9+
{
10+
private readonly List<PriceListItemData> _items;
11+
12+
private readonly MoneyValue _discountValue;
13+
14+
public DiscountedValueFromPriceListPricingStrategy(
15+
List<PriceListItemData> items,
16+
MoneyValue discountValue)
17+
{
18+
_items = items;
19+
_discountValue = discountValue;
20+
}
21+
22+
public MoneyValue GetPrice(string countryCode, SubscriptionPeriod subscriptionPeriod, PriceListItemCategory category)
23+
{
24+
var priceListItem = _items.Single(x =>
25+
x.CountryCode == countryCode && x.SubscriptionPeriod == subscriptionPeriod &&
26+
x.Category == category);
27+
28+
return priceListItem.Value - _discountValue;
29+
}
30+
}
31+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
2+
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
3+
4+
namespace CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies
5+
{
6+
public interface IPricingStrategy
7+
{
8+
MoneyValue GetPrice(
9+
string countryCode,
10+
SubscriptionPeriod subscriptionPeriod,
11+
PriceListItemCategory category);
12+
}
13+
}

src/Modules/Payments/Domain/SeedWork/MoneyValue.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,12 @@ public static MoneyValue Of(decimal value, string currency)
3737
public static bool operator >=(MoneyValue left, decimal right) => left.Value >= right;
3838

3939
public static bool operator <=(MoneyValue left, decimal right) => left.Value <= right;
40+
41+
public static MoneyValue operator -(MoneyValue left, MoneyValue right)
42+
{
43+
CheckRule(new MoneyMustHaveTheSameCurrencyRule(left, right));
44+
45+
return Of(left.Value - right.Value, left.Currency);
46+
}
4047
}
4148
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using CompanyName.MyMeetings.BuildingBlocks.Domain;
2+
3+
namespace CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork.Rules
4+
{
5+
public class MoneyMustHaveTheSameCurrencyRule : IBusinessRule
6+
{
7+
private readonly MoneyValue _left;
8+
9+
private readonly MoneyValue _right;
10+
11+
public MoneyMustHaveTheSameCurrencyRule(MoneyValue left, MoneyValue right)
12+
{
13+
_left = left;
14+
_right = right;
15+
}
16+
17+
public bool IsBroken() => _left.Currency != _right.Currency;
18+
19+
public string Message => "Currency of money must be the same.";
20+
}
21+
}

src/Modules/Payments/Tests/UnitTests/SubscriptionPayments/SubscriptionPaymentTestsBase.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using CompanyName.MyMeetings.Modules.Payments.Domain.Payers;
44
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems;
5+
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies;
56
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
67
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
78
using CompanyName.MyMeetings.Modules.Payments.Domain.UnitTests.SeedWork;
@@ -48,7 +49,8 @@ private PriceList CreatePriceList()
4849
MoneyValue.Of(60, "PLN"),
4950
PriceListItemCategory.New);
5051

51-
var priceList = PriceList.CreateFromItems(new List<PriceListItemData> { priceListItem });
52+
var priceListItems = new List<PriceListItemData> { priceListItem };
53+
var priceList = PriceList.Create(priceListItems, new DirectValueFromPriceListPricingStrategy(priceListItems));
5254

5355
return priceList;
5456
}

src/Modules/Payments/Tests/UnitTests/SubscriptionRenewalPayments/SubscriptionRenewalPaymentTestsBase.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using CompanyName.MyMeetings.Modules.Payments.Domain.Payers;
44
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems;
5+
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies;
56
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
67
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
78
using CompanyName.MyMeetings.Modules.Payments.Domain.UnitTests.SeedWork;
@@ -48,7 +49,8 @@ private PriceList CreatePriceList()
4849
MoneyValue.Of(60, "PLN"),
4950
PriceListItemCategory.Renewal);
5051

51-
var priceList = PriceList.CreateFromItems(new List<PriceListItemData> { priceListItem });
52+
var priceListItems = new List<PriceListItemData> { priceListItem };
53+
var priceList = PriceList.Create(priceListItems, new DirectValueFromPriceListPricingStrategy(priceListItems));
5254

5355
return priceList;
5456
}

0 commit comments

Comments
 (0)