Skip to content

Commit

Permalink
Add Pricing Strategies for Subscriptions (Strategy Pattern)
Browse files Browse the repository at this point in the history
  • Loading branch information
kgrzybek committed Apr 20, 2021
1 parent 78e3ec5 commit c5e3b90
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public GetPriceListItemsQueryHandler(ISqlConnectionFactory sqlConnectionFactory)

public async Task<List<PriceListItemDto>> Handle(GetPriceListItemsQuery request, CancellationToken cancellationToken)
{
return await PriceListProvider.GetPriceListItems(_sqlConnectionFactory.GetOpenConnection());
return await PriceListFactory.GetPriceListItems(_sqlConnectionFactory.GetOpenConnection());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,34 @@
using System.Linq;
using System.Threading.Tasks;
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems;
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies;
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
using Dapper;

namespace CompanyName.MyMeetings.Modules.Payments.Application.PriceListItems
{
public static class PriceListProvider
public static class PriceListFactory
{
public static async Task<PriceList> GetPriceList(IDbConnection connection)
public static async Task<PriceList> CreatePriceList(IDbConnection connection)
{
var priceListItemList = await GetPriceListItems(connection);

return PriceList.CreateFromItems(
priceListItemList
.Select(x =>
new PriceListItemData(
x.CountryCode,
SubscriptionPeriod.Of(x.SubscriptionPeriodCode),
MoneyValue.Of(x.MoneyValue, x.MoneyCurrency),
PriceListItemCategory.Of(x.CategoryCode)))
.ToList());
var priceListItems = priceListItemList
.Select(x =>
new PriceListItemData(
x.CountryCode,
SubscriptionPeriod.Of(x.SubscriptionPeriodCode),
MoneyValue.Of(x.MoneyValue, x.MoneyCurrency),
PriceListItemCategory.Of(x.CategoryCode)))
.ToList();

// This is place for selecting pricing strategy based on provided data and the system state.
IPricingStrategy pricingStrategy = new DirectValueFromPriceListPricingStrategy(priceListItems);

return PriceList.Create(
priceListItems,
pricingStrategy);
}

public static async Task<List<PriceListItemDto>> GetPriceListItems(IDbConnection connection)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ internal BuySubscriptionCommandHandler(

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

var subscription = SubscriptionPayment.Buy(
_payerContext.PayerId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ internal BuySubscriptionRenewalCommandHandler(

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

var subscriptionId = new SubscriptionId(command.SubscriptionId);

Expand Down
21 changes: 12 additions & 9 deletions src/Modules/Payments/Domain/PriceListItems/PriceList.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using CompanyName.MyMeetings.BuildingBlocks.Domain;
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies;
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
using CompanyName.MyMeetings.Modules.Payments.Domain.SubscriptionPayments.Rules;
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
Expand All @@ -11,14 +11,21 @@ public class PriceList : ValueObject
{
private readonly List<PriceListItemData> _items;

private PriceList(List<PriceListItemData> items)
private readonly IPricingStrategy _pricingStrategy;

private PriceList(
List<PriceListItemData> items,
IPricingStrategy pricingStrategy)
{
_items = items;
_pricingStrategy = pricingStrategy;
}

public static PriceList CreateFromItems(List<PriceListItemData> items)
public static PriceList Create(
List<PriceListItemData> items,
IPricingStrategy pricingStrategy)
{
return new PriceList(items);
return new PriceList(items, pricingStrategy);
}

public MoneyValue GetPrice(
Expand All @@ -28,11 +35,7 @@ public MoneyValue GetPrice(
{
CheckRule(new PriceForSubscriptionMustBeDefinedRule(countryCode, subscriptionPeriod, _items, category));

var priceListItem = _items.Single(x =>
x.CountryCode == countryCode && x.SubscriptionPeriod == subscriptionPeriod &&
x.Category == category);

return priceListItem.Value;
return _pricingStrategy.GetPrice(countryCode, subscriptionPeriod, category);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Linq;
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;

namespace CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies
{
public class DirectValueFromPriceListPricingStrategy : IPricingStrategy
{
private readonly List<PriceListItemData> _items;

public DirectValueFromPriceListPricingStrategy(List<PriceListItemData> items)
{
_items = items;
}

public MoneyValue GetPrice(
string countryCode,
SubscriptionPeriod subscriptionPeriod,
PriceListItemCategory category)
{
var priceListItem = _items.Single(x =>
x.CountryCode == countryCode && x.SubscriptionPeriod == subscriptionPeriod &&
x.Category == category);

return priceListItem.Value;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;

namespace CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies
{
public class DirectValuePricingStrategy : IPricingStrategy
{
private readonly MoneyValue _directValue;

public DirectValuePricingStrategy(MoneyValue directValue)
{
_directValue = directValue;
}

public MoneyValue GetPrice(string countryCode, SubscriptionPeriod subscriptionPeriod, PriceListItemCategory category)
{
return _directValue;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Collections.Generic;
using System.Linq;
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;

namespace CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies
{
public class DiscountedValueFromPriceListPricingStrategy : IPricingStrategy
{
private readonly List<PriceListItemData> _items;

private readonly MoneyValue _discountValue;

public DiscountedValueFromPriceListPricingStrategy(
List<PriceListItemData> items,
MoneyValue discountValue)
{
_items = items;
_discountValue = discountValue;
}

public MoneyValue GetPrice(string countryCode, SubscriptionPeriod subscriptionPeriod, PriceListItemCategory category)
{
var priceListItem = _items.Single(x =>
x.CountryCode == countryCode && x.SubscriptionPeriod == subscriptionPeriod &&
x.Category == category);

return priceListItem.Value - _discountValue;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;

namespace CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies
{
public interface IPricingStrategy
{
MoneyValue GetPrice(
string countryCode,
SubscriptionPeriod subscriptionPeriod,
PriceListItemCategory category);
}
}
7 changes: 7 additions & 0 deletions src/Modules/Payments/Domain/SeedWork/MoneyValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,12 @@ public static MoneyValue Of(decimal value, string currency)
public static bool operator >=(MoneyValue left, decimal right) => left.Value >= right;

public static bool operator <=(MoneyValue left, decimal right) => left.Value <= right;

public static MoneyValue operator -(MoneyValue left, MoneyValue right)
{
CheckRule(new MoneyMustHaveTheSameCurrencyRule(left, right));

return Of(left.Value - right.Value, left.Currency);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using CompanyName.MyMeetings.BuildingBlocks.Domain;

namespace CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork.Rules
{
public class MoneyMustHaveTheSameCurrencyRule : IBusinessRule
{
private readonly MoneyValue _left;

private readonly MoneyValue _right;

public MoneyMustHaveTheSameCurrencyRule(MoneyValue left, MoneyValue right)
{
_left = left;
_right = right;
}

public bool IsBroken() => _left.Currency != _right.Currency;

public string Message => "Currency of money must be the same.";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using CompanyName.MyMeetings.Modules.Payments.Domain.Payers;
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems;
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies;
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
using CompanyName.MyMeetings.Modules.Payments.Domain.UnitTests.SeedWork;
Expand Down Expand Up @@ -48,7 +49,8 @@ private PriceList CreatePriceList()
MoneyValue.Of(60, "PLN"),
PriceListItemCategory.New);

var priceList = PriceList.CreateFromItems(new List<PriceListItemData> { priceListItem });
var priceListItems = new List<PriceListItemData> { priceListItem };
var priceList = PriceList.Create(priceListItems, new DirectValueFromPriceListPricingStrategy(priceListItems));

return priceList;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using CompanyName.MyMeetings.Modules.Payments.Domain.Payers;
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems;
using CompanyName.MyMeetings.Modules.Payments.Domain.PriceListItems.PricingStrategies;
using CompanyName.MyMeetings.Modules.Payments.Domain.SeedWork;
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
using CompanyName.MyMeetings.Modules.Payments.Domain.UnitTests.SeedWork;
Expand Down Expand Up @@ -48,7 +49,8 @@ private PriceList CreatePriceList()
MoneyValue.Of(60, "PLN"),
PriceListItemCategory.Renewal);

var priceList = PriceList.CreateFromItems(new List<PriceListItemData> { priceListItem });
var priceListItems = new List<PriceListItemData> { priceListItem };
var priceList = PriceList.Create(priceListItems, new DirectValueFromPriceListPricingStrategy(priceListItems));

return priceList;
}
Expand Down

0 comments on commit c5e3b90

Please sign in to comment.