Skip to content

Commit

Permalink
VCST-1303: add cache to CartAggregate repository (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
ksavosteev authored Jul 29, 2024
1 parent 2d4c624 commit b4b125a
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Threading.Tasks;
using AutoMapper;
using PipelineNet.Middleware;
using VirtoCommerce.CartModule.Core.Model;
using VirtoCommerce.CartModule.Core.Model.Search;
using VirtoCommerce.CoreModule.Core.Common;
using VirtoCommerce.MarketingModule.Core.Model.Promotions;
Expand Down Expand Up @@ -81,6 +82,7 @@ protected virtual ShoppingCartSearchCriteria GetCartSearchCriteria(PromotionEval
cartSearchCriteria.CustomerId = context.CustomerId;
cartSearchCriteria.OrganizationId = context.OrganizaitonId;
cartSearchCriteria.Currency = context.Currency;
cartSearchCriteria.ResponseGroup = CartResponseGroup.Full.ToString();

return cartSearchCriteria;
}
Expand All @@ -94,6 +96,7 @@ protected virtual ShoppingCartSearchCriteria GetCartSearchCriteria(PriceEvaluati
cartSearchCriteria.CustomerId = context.CustomerId;
cartSearchCriteria.OrganizationId = context.OrganizationId;
cartSearchCriteria.Currency = context.Currency;
cartSearchCriteria.ResponseGroup = CartResponseGroup.Full.ToString();

return cartSearchCriteria;
}
Expand All @@ -107,6 +110,7 @@ protected virtual ShoppingCartSearchCriteria GetCartSearchCriteria(TaxEvaluation
cartSearchCriteria.CustomerId = context.CustomerId;
cartSearchCriteria.OrganizationId = context.OrganizationId;
cartSearchCriteria.Currency = context.Currency;
cartSearchCriteria.ResponseGroup = CartResponseGroup.Full.ToString();

return cartSearchCriteria;
}
Expand Down
80 changes: 59 additions & 21 deletions src/VirtoCommerce.XCart.Data/Services/CartAggregateRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using VirtoCommerce.CartModule.Core.Model;
using VirtoCommerce.CartModule.Core.Model.Search;
using VirtoCommerce.CartModule.Core.Services;
using VirtoCommerce.CoreModule.Core.Common;
using VirtoCommerce.CoreModule.Core.Currency;
using VirtoCommerce.CustomerModule.Core.Services;
using VirtoCommerce.Platform.Caching;
using VirtoCommerce.Platform.Core.Caching;
using VirtoCommerce.Platform.Core.Common;
using VirtoCommerce.StoreModule.Core.Services;
using VirtoCommerce.Xapi.Core.Extensions;
Expand All @@ -30,14 +33,18 @@ public class CartAggregateRepository : ICartAggregateRepository
private readonly IMemberResolver _memberResolver;
private readonly IStoreService _storeService;

private readonly IPlatformMemoryCache _platformMemoryCache;

public CartAggregateRepository(
Func<CartAggregate> cartAggregateFactory,
IShoppingCartSearchService shoppingCartSearchService,
IShoppingCartService shoppingCartService,
ICurrencyService currencyService,
IMemberResolver memberResolver,
IStoreService storeService,
ICartProductService cartProductsService)
ICartProductService cartProductsService,
IPlatformMemoryCache platformMemoryCache
)
{
_cartAggregateFactory = cartAggregateFactory;
_shoppingCartSearchService = shoppingCartSearchService;
Expand All @@ -46,6 +53,7 @@ public CartAggregateRepository(
_memberResolver = memberResolver;
_storeService = storeService;
_cartProductsService = cartProductsService;
_platformMemoryCache = platformMemoryCache;
}

public virtual async Task SaveAsync(CartAggregate cartAggregate)
Expand All @@ -54,6 +62,9 @@ public virtual async Task SaveAsync(CartAggregate cartAggregate)
cartAggregate.Cart.ModifiedDate = DateTime.UtcNow;

await _shoppingCartService.SaveChangesAsync(new List<ShoppingCart> { cartAggregate.Cart });

// Clear cache
GenericCachingRegion<CartAggregate>.ExpireTokenForKey(cartAggregate.Id);
}

public async Task<CartAggregate> GetCartByIdAsync(string cartId, string cultureName = null)
Expand All @@ -76,7 +87,7 @@ public async Task<CartAggregate> GetCartByIdAsync(string cartId, string response
var cart = await _shoppingCartService.GetByIdAsync(cartId, responseGroup);
if (cart != null)
{
return await InnerGetCartAggregateFromCartAsync(cart, cultureName ?? Language.InvariantLanguage.CultureName, productsIncludeFields);
return await InnerGetCartAggregateFromCartAsync(cart, cultureName ?? Language.InvariantLanguage.CultureName, productsIncludeFields, CartResponseGroup.Full.ToString());
}
return null;
}
Expand All @@ -88,7 +99,7 @@ public Task<CartAggregate> GetCartForShoppingCartAsync(ShoppingCart cart, string
return Task.FromResult(cartAggregate);
}

return InnerGetCartAggregateFromCartAsync(cart, cultureName ?? Language.InvariantLanguage.CultureName);
return InnerGetCartAggregateFromCartAsync(cart, cultureName ?? Language.InvariantLanguage.CultureName, CartResponseGroup.Full.ToString());
}

public async Task<CartAggregate> GetCartAsync(ICartRequest cartRequest, string responseGroup = null)
Expand Down Expand Up @@ -116,7 +127,7 @@ public async Task<CartAggregate> GetCartAsync(ICartRequest cartRequest, string r
var cart = cartSearchResult.Results.FirstOrDefault(x => cartRequest.CartType != null || x.Type == null);
if (cart != null)
{
return await InnerGetCartAggregateFromCartAsync(cart.Clone() as ShoppingCart, cartRequest.CultureName);
return await InnerGetCartAggregateFromCartAsync(cart.Clone() as ShoppingCart, cartRequest.CultureName, criteria.ResponseGroup);
}

return null;
Expand All @@ -138,7 +149,7 @@ public async Task<CartAggregate> GetCartAsync(ShoppingCartSearchCriteria criteri
var cart = cartSearchResult.Results.FirstOrDefault(x => criteria.Type != null || x.Type == null);
if (cart != null)
{
return await InnerGetCartAggregateFromCartAsync(cart.Clone() as ShoppingCart, cultureName ?? Language.InvariantLanguage.CultureName);
return await InnerGetCartAggregateFromCartAsync(cart.Clone() as ShoppingCart, cultureName ?? Language.InvariantLanguage.CultureName, criteria.ResponseGroup);
}

return null;
Expand All @@ -154,19 +165,58 @@ public async Task<SearchCartResponse> SearchCartAsync(ShoppingCartSearchCriteria
ArgumentNullException.ThrowIfNull(criteria);

var searchResult = await _shoppingCartSearchService.SearchAsync(criteria);
var cartAggregates = await GetCartsForShoppingCartsAsync(searchResult.Results, productsIncludeFields);
var cartAggregates = await GetCartsForShoppingCartsAsync(criteria, searchResult.Results, productsIncludeFields);

return new SearchCartResponse { Results = cartAggregates, TotalCount = searchResult.TotalCount };
}

public virtual Task RemoveCartAsync(string cartId) => _shoppingCartService.DeleteAsync(new[] { cartId }, softDelete: true);
public virtual async Task RemoveCartAsync(string cartId)
{
await _shoppingCartService.DeleteAsync(new[] { cartId }, softDelete: true);
GenericCachingRegion<CartAggregate>.ExpireTokenForKey(cartId);
}

protected virtual async Task<CartAggregate> InnerGetCartAggregateFromCartAsync(ShoppingCart cart, string language, CartAggregateResponseGroup responseGroup = CartAggregateResponseGroup.Full)
protected virtual async Task<IList<CartAggregate>> GetCartsForShoppingCartsAsync(ShoppingCartSearchCriteria criteria, IList<ShoppingCart> carts, IList<string> productsIncludeFields, string cultureName = null)
{
var result = new List<CartAggregate>();

foreach (var shoppingCart in carts)
{
result.Add(await InnerGetCartAggregateFromCartAsync(shoppingCart, cultureName ?? Language.InvariantLanguage.CultureName, productsIncludeFields, criteria.ResponseGroup));
}

return result;
}

protected virtual async Task<CartAggregate> InnerGetCartAggregateFromCartAsync(ShoppingCart cart, string language, string responseGroup)
{
return await InnerGetCartAggregateFromCartAsync(cart, language, null, responseGroup);
}

protected virtual async Task<CartAggregate> InnerGetCartAggregateFromCartAsync(ShoppingCart cart, string language, IList<string> productsIncludeFields, CartAggregateResponseGroup responseGroup = CartAggregateResponseGroup.Full)
protected virtual async Task<CartAggregate> InnerGetCartAggregateFromCartAsync(ShoppingCart cart, string language, IList<string> productsIncludeFields, string responseGroup)
{
if (string.IsNullOrEmpty(cart.Id))
{
return await InnerGetCartAggregateFromCartNoCacheAsync(cart, language, productsIncludeFields);
}

var cacheKey = CacheKey.With(GetType(),
nameof(InnerGetCartAggregateFromCartAsync),
cart.Id,
language,
responseGroup ?? string.Empty,
!productsIncludeFields.IsNullOrEmpty() ? string.Join(',', productsIncludeFields) : string.Empty);

var result = await _platformMemoryCache.GetOrCreateExclusiveAsync(cacheKey, async cacheOptions =>
{
cacheOptions.AddExpirationToken(GenericCachingRegion<CartAggregate>.CreateChangeTokenForKey(cart.Id));
return await InnerGetCartAggregateFromCartNoCacheAsync(cart, language, productsIncludeFields);
});

return result;
}

private async Task<CartAggregate> InnerGetCartAggregateFromCartNoCacheAsync(ShoppingCart cart, string language, IList<string> productsIncludeFields)
{
ArgumentNullException.ThrowIfNull(cart);

Expand Down Expand Up @@ -248,17 +298,5 @@ protected virtual async Task<CartAggregate> InnerGetCartAggregateFromCartAsync(S
return aggregate;
}
}

protected virtual async Task<IList<CartAggregate>> GetCartsForShoppingCartsAsync(IList<ShoppingCart> carts, IList<string> productsIncludeFields, string cultureName = null)
{
var result = new List<CartAggregate>();

foreach (var shoppingCart in carts)
{
result.Add(await InnerGetCartAggregateFromCartAsync(shoppingCart, cultureName ?? Language.InvariantLanguage.CultureName, productsIncludeFields));
}

return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
using System.Threading.Tasks;
using AutoFixture;
using FluentAssertions;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using VirtoCommerce.CartModule.Core.Model;
using VirtoCommerce.CartModule.Core.Model.Search;
using VirtoCommerce.CartModule.Core.Services;
using VirtoCommerce.CoreModule.Core.Currency;
using VirtoCommerce.CustomerModule.Core.Model;
using VirtoCommerce.CustomerModule.Core.Services;
using VirtoCommerce.Platform.Caching;
using VirtoCommerce.PricingModule.Core.Model;
using VirtoCommerce.StoreModule.Core.Model;
using VirtoCommerce.StoreModule.Core.Services;
Expand All @@ -29,6 +33,7 @@ public class CartAggregateRepositoryTests : XCartMoqHelper
private readonly Mock<ICurrencyService> _currencyService;
private readonly Mock<IStoreService> _storeService;
private readonly Mock<IMemberResolver> _memberResolver;
private readonly PlatformMemoryCache _platformMemoryCache;

private readonly CartAggregateRepository repository;

Expand All @@ -40,13 +45,19 @@ public CartAggregateRepositoryTests()
_storeService = new Mock<IStoreService>();
_memberResolver = new Mock<IMemberResolver>();

_platformMemoryCache = new PlatformMemoryCache(
new MemoryCache(Options.Create(new MemoryCacheOptions())),
Options.Create(new CachingOptions()),
new Mock<ILogger<PlatformMemoryCache>>().Object);

repository = new CartAggregateRepository(
() => _fixture.Create<CartAggregate>(),
_shoppingCartSearchService.Object,
_shoppingCartService.Object,
_currencyService.Object,
_memberResolver.Object,
_storeService.Object,
null,
null
);
}
Expand Down Expand Up @@ -152,7 +163,8 @@ public async Task GetCartForShoppingCartAsync_CartFound_AggregateReturnedCorrect
_currencyService.Object,
_memberResolver.Object,
_storeService.Object,
_cartProductServiceMock.Object
_cartProductServiceMock.Object,
_platformMemoryCache
);

var storeId = "Store";
Expand Down Expand Up @@ -201,7 +213,8 @@ public async Task GetCartForShoppingCartAsync_ProductPriceChanged_ShouldContainW
_currencyService.Object,
_memberResolver.Object,
_storeService.Object,
_cartProductServiceMock.Object);
_cartProductServiceMock.Object,
_platformMemoryCache);

var storeId = "Store";
var store = _fixture.Create<Store>();
Expand Down

0 comments on commit b4b125a

Please sign in to comment.