Skip to content

Commit

Permalink
Feature/respect encapsulation (dotnet-architecture#349)
Browse files Browse the repository at this point in the history
* resolve osbsolete method

* put all properties as private, align unit test

* fix version of version in MD, add instruction to install ef tool

* fix url stored
  • Loading branch information
michelcedric authored Feb 3, 2020
1 parent 288d827 commit 3e22803
Show file tree
Hide file tree
Showing 22 changed files with 162 additions and 99 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The **eShopOnWeb** sample is related to the [eShopOnContainers](https://github.c
The goal for this sample is to demonstrate some of the principles and patterns described in the [eBook](https://aka.ms/webappebook). It is not meant to be an eCommerce reference application, and as such it does not implement many features that would be obvious and/or essential to a real eCommerce application.

> ### VERSIONS
> #### The `master` branch is currently running ASP.NET Core 2.2.
> #### The `master` branch is currently running ASP.NET Core 3.1.
> #### Older versions are tagged.
## Topics (eBook TOC)
Expand Down Expand Up @@ -58,6 +58,10 @@ You can also run the samples in Docker (see below).
```

1. Ensure your connection strings in `appsettings.json` point to a local SQL Server instance.
1. Ensure the tool EF was already installed. You can find some help [here](https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/dotnet)
```
dotnet tool install --global dotnet-ef
```

1. Open a command prompt in the Web folder and execute the following commands:

Expand Down
4 changes: 2 additions & 2 deletions src/ApplicationCore/Entities/BaseEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
{
// This can easily be modified to be BaseEntity<T> and public T Id to support different key types.
// Using non-generic integer types for simplicity and to ease caching logic
public class BaseEntity
public abstract class BaseEntity
{
public int Id { get; set; }
public virtual int Id { get; protected set; }
}
}
21 changes: 13 additions & 8 deletions src/ApplicationCore/Entities/BasketAggregate/Basket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,34 @@ namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate
{
public class Basket : BaseEntity, IAggregateRoot
{
public string BuyerId { get; set; }
public string BuyerId { get; private set; }
private readonly List<BasketItem> _items = new List<BasketItem>();
public IReadOnlyCollection<BasketItem> Items => _items.AsReadOnly();

public Basket(string buyerId)
{
BuyerId = buyerId;
}

public void AddItem(int catalogItemId, decimal unitPrice, int quantity = 1)
{
if (!Items.Any(i => i.CatalogItemId == catalogItemId))
{
_items.Add(new BasketItem()
{
CatalogItemId = catalogItemId,
Quantity = quantity,
UnitPrice = unitPrice
});
_items.Add(new BasketItem(catalogItemId, quantity, unitPrice));
return;
}
var existingItem = Items.FirstOrDefault(i => i.CatalogItemId == catalogItemId);
existingItem.Quantity += quantity;
existingItem.AddQuantity(quantity);
}

public void RemoveEmptyItems()
{
_items.RemoveAll(i => i.Quantity == 0);
}

public void SetNewBuyerId(string buyerId)
{
BuyerId = buyerId;
}
}
}
24 changes: 21 additions & 3 deletions src/ApplicationCore/Entities/BasketAggregate/BasketItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,27 @@
{
public class BasketItem : BaseEntity
{
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public int CatalogItemId { get; set; }

public decimal UnitPrice { get; private set; }
public int Quantity { get; private set; }
public int CatalogItemId { get; private set; }
public int BasketId { get; private set; }

public BasketItem(int catalogItemId, int quantity, decimal unitPrice)
{
CatalogItemId = catalogItemId;
Quantity = quantity;
UnitPrice = unitPrice;
}

public void AddQuantity(int quantity)
{
Quantity += quantity;
}

public void SetNewQuantity(int quantity)
{
Quantity = quantity;
}
}
}
6 changes: 3 additions & 3 deletions src/ApplicationCore/Entities/BuyerAggregate/PaymentMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
{
public class PaymentMethod : BaseEntity
{
public string Alias { get; set; }
public string CardId { get; set; } // actual card data must be stored in a PCI compliant system, like Stripe
public string Last4 { get; set; }
public string Alias { get; private set; }
public string CardId { get; private set; } // actual card data must be stored in a PCI compliant system, like Stripe
public string Last4 { get; private set; }
}
}
6 changes: 5 additions & 1 deletion src/ApplicationCore/Entities/CatalogBrand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ namespace Microsoft.eShopWeb.ApplicationCore.Entities
{
public class CatalogBrand : BaseEntity, IAggregateRoot
{
public string Brand { get; set; }
public string Brand { get; private set; }
public CatalogBrand(string brand)
{
Brand = brand;
}
}
}
36 changes: 27 additions & 9 deletions src/ApplicationCore/Entities/CatalogItem.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Ardalis.GuardClauses;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;

namespace Microsoft.eShopWeb.ApplicationCore.Entities
{
public class CatalogItem : BaseEntity, IAggregateRoot
{
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string PictureUri { get; set; }
public int CatalogTypeId { get; set; }
public CatalogType CatalogType { get; set; }
public int CatalogBrandId { get; set; }
public CatalogBrand CatalogBrand { get; set; }
public string Name { get; private set; }
public string Description { get; private set; }
public decimal Price { get; private set; }
public string PictureUri { get; private set; }
public int CatalogTypeId { get; private set; }
public CatalogType CatalogType { get; private set; }
public int CatalogBrandId { get; private set; }
public CatalogBrand CatalogBrand { get; private set; }

public CatalogItem(int catalogTypeId, int catalogBrandId, string description, string name, decimal price, string pictureUri)
{
CatalogTypeId = catalogTypeId;
CatalogBrandId = catalogBrandId;
Description = description;
Name = name;
Price = price;
PictureUri = pictureUri;
}

public void Update(string name, decimal price)
{
Guard.Against.NullOrEmpty(name, nameof(name));
Name = name;
Price = price;
}
}
}
6 changes: 5 additions & 1 deletion src/ApplicationCore/Entities/CatalogType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ namespace Microsoft.eShopWeb.ApplicationCore.Entities
{
public class CatalogType : BaseEntity, IAggregateRoot
{
public string Type { get; set; }
public string Type { get; private set; }
public CatalogType(string type)
{
Type = type;
}
}
}
10 changes: 5 additions & 5 deletions src/ApplicationCore/Entities/OrderAggregate/Address.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
{
public class Address // ValueObject
{
public String Street { get; private set; }
public string Street { get; private set; }

public String City { get; private set; }
public string City { get; private set; }

public String State { get; private set; }
public string State { get; private set; }

public String Country { get; private set; }
public string Country { get; private set; }

public String ZipCode { get; private set; }
public string ZipCode { get; private set; }

private Address() { }

Expand Down
6 changes: 3 additions & 3 deletions src/ApplicationCore/Services/BasketService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ public async Task SetQuantities(int basketId, Dictionary<string, int> quantities
{
if (quantities.TryGetValue(item.Id.ToString(), out var quantity))
{
if(_logger != null) _logger.LogInformation($"Updating quantity of item ID:{item.Id} to {quantity}.");
item.Quantity = quantity;
if (_logger != null) _logger.LogInformation($"Updating quantity of item ID:{item.Id} to {quantity}.");
item.SetNewQuantity(quantity);
}
}
basket.RemoveEmptyItems();
Expand All @@ -74,7 +74,7 @@ public async Task TransferBasketAsync(string anonymousId, string userName)
var basketSpec = new BasketWithItemsSpecification(anonymousId);
var basket = (await _basketRepository.ListAsync(basketSpec)).FirstOrDefault();
if (basket == null) return;
basket.BuyerId = userName;
basket.SetNewBuyerId(userName);
await _basketRepository.UpdateAsync(basket);
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/ApplicationCore/Services/OrderService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ namespace Microsoft.eShopWeb.ApplicationCore.Services
public class OrderService : IOrderService
{
private readonly IAsyncRepository<Order> _orderRepository;
private readonly IUriComposer _uriComposer;
private readonly IAsyncRepository<Basket> _basketRepository;
private readonly IAsyncRepository<CatalogItem> _itemRepository;

public OrderService(IAsyncRepository<Basket> basketRepository,
IAsyncRepository<CatalogItem> itemRepository,
IAsyncRepository<Order> orderRepository)
IAsyncRepository<Order> orderRepository,
IUriComposer uriComposer)
{
_orderRepository = orderRepository;
_uriComposer = uriComposer;
_basketRepository = basketRepository;
_itemRepository = itemRepository;
}
Expand All @@ -31,7 +34,7 @@ public async Task CreateOrderAsync(int basketId, Address shippingAddress)
foreach (var item in basket.Items)
{
var catalogItem = await _itemRepository.GetByIdAsync(item.CatalogItemId);
var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, catalogItem.PictureUri);
var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name,_uriComposer.ComposePicUri(catalogItem.PictureUri));
var orderItem = new OrderItem(itemOrdered, item.UnitPrice, item.Quantity);
items.Add(orderItem);
}
Expand Down
42 changes: 21 additions & 21 deletions src/Infrastructure/Data/CatalogContextSeed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,41 +59,41 @@ static IEnumerable<CatalogBrand> GetPreconfiguredCatalogBrands()
{
return new List<CatalogBrand>()
{
new CatalogBrand() { Brand = "Azure"},
new CatalogBrand() { Brand = ".NET" },
new CatalogBrand() { Brand = "Visual Studio" },
new CatalogBrand() { Brand = "SQL Server" },
new CatalogBrand() { Brand = "Other" }
new CatalogBrand("Azure"),
new CatalogBrand(".NET"),
new CatalogBrand("Visual Studio"),
new CatalogBrand("SQL Server"),
new CatalogBrand("Other")
};
}

static IEnumerable<CatalogType> GetPreconfiguredCatalogTypes()
{
return new List<CatalogType>()
{
new CatalogType() { Type = "Mug"},
new CatalogType() { Type = "T-Shirt" },
new CatalogType() { Type = "Sheet" },
new CatalogType() { Type = "USB Memory Stick" }
new CatalogType("Mug"),
new CatalogType("T-Shirt"),
new CatalogType("Sheet"),
new CatalogType("USB Memory Stick")
};
}

static IEnumerable<CatalogItem> GetPreconfiguredItems()
{
return new List<CatalogItem>()
{
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Sweatshirt", Name = ".NET Bot Black Sweatshirt", Price = 19.5M, PictureUri = "http://catalogbaseurltobereplaced/images/products/1.png" },
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=2, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureUri = "http://catalogbaseurltobereplaced/images/products/2.png" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://catalogbaseurltobereplaced/images/products/3.png" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation Sweatshirt", Name = ".NET Foundation Sweatshirt", Price = 12, PictureUri = "http://catalogbaseurltobereplaced/images/products/4.png" },
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=5, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureUri = "http://catalogbaseurltobereplaced/images/products/5.png" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Sweatshirt", Name = ".NET Blue Sweatshirt", Price = 12, PictureUri = "http://catalogbaseurltobereplaced/images/products/6.png" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://catalogbaseurltobereplaced/images/products/7.png" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Sweatshirt", Name = "Kudu Purple Sweatshirt", Price = 8.5M, PictureUri = "http://catalogbaseurltobereplaced/images/products/8.png" },
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=5, Description = "Cup<T> White Mug", Name = "Cup<T> White Mug", Price = 12, PictureUri = "http://catalogbaseurltobereplaced/images/products/9.png" },
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureUri = "http://catalogbaseurltobereplaced/images/products/10.png" },
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = "Cup<T> Sheet", Name = "Cup<T> Sheet", Price = 8.5M, PictureUri = "http://catalogbaseurltobereplaced/images/products/11.png" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White TShirt", Name = "Prism White TShirt", Price = 12, PictureUri = "http://catalogbaseurltobereplaced/images/products/12.png" }
new CatalogItem(2,2, ".NET Bot Black Sweatshirt", ".NET Bot Black Sweatshirt", 19.5M, "http://catalogbaseurltobereplaced/images/products/1.png"),
new CatalogItem(1,2, ".NET Black & White Mug", ".NET Black & White Mug", 8.50M, "http://catalogbaseurltobereplaced/images/products/2.png"),
new CatalogItem(2,5, "Prism White T-Shirt", "Prism White T-Shirt", 12, "http://catalogbaseurltobereplaced/images/products/3.png"),
new CatalogItem(2,2, ".NET Foundation Sweatshirt", ".NET Foundation Sweatshirt", 12, "http://catalogbaseurltobereplaced/images/products/4.png"),
new CatalogItem(3,5, "Roslyn Red Sheet", "Roslyn Red Sheet", 8.5M, "http://catalogbaseurltobereplaced/images/products/5.png"),
new CatalogItem(2,2, ".NET Blue Sweatshirt", ".NET Blue Sweatshirt", 12, "http://catalogbaseurltobereplaced/images/products/6.png"),
new CatalogItem(2,5, "Roslyn Red T-Shirt", "Roslyn Red T-Shirt", 12, "http://catalogbaseurltobereplaced/images/products/7.png"),
new CatalogItem(2,5, "Kudu Purple Sweatshirt", "Kudu Purple Sweatshirt", 8.5M, "http://catalogbaseurltobereplaced/images/products/8.png"),
new CatalogItem(1,5, "Cup<T> White Mug", "Cup<T> White Mug", 12, "http://catalogbaseurltobereplaced/images/products/9.png"),
new CatalogItem(3,2, ".NET Foundation Sheet", ".NET Foundation Sheet", 12, "http://catalogbaseurltobereplaced/images/products/10.png"),
new CatalogItem(3,2, "Cup<T> Sheet", "Cup<T> Sheet", 8.5M, "http://catalogbaseurltobereplaced/images/products/11.png"),
new CatalogItem(2,5, "Prism White TShirt", "Prism White TShirt", 12, "http://catalogbaseurltobereplaced/images/products/12.png")
};
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Infrastructure/Data/Config/CatalogTypeConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public void Configure(EntityTypeBuilder<CatalogType> builder)
builder.HasKey(ci => ci.Id);

builder.Property(ci => ci.Id)
.ForSqlServerUseSequenceHiLo("catalog_type_hilo")
.UseHiLo("catalog_type_hilo")
.IsRequired();

builder.Property(cb => cb.Type)
Expand Down
2 changes: 1 addition & 1 deletion src/Web/Pages/Admin/EditCatalogItem.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public EditCatalogItemModel(ICatalogItemViewModelService catalogItemViewModelSer
[BindProperty]
public CatalogItemViewModel CatalogModel { get; set; } = new CatalogItemViewModel();

public async Task OnGet(CatalogItemViewModel catalogModel)
public void OnGet(CatalogItemViewModel catalogModel)
{
CatalogModel = catalogModel;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Web/Services/BasketViewModelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private async Task<BasketViewModel> CreateViewModelFromBasket(Basket basket)

private async Task<BasketViewModel> CreateBasketForUser(string userId)
{
var basket = new Basket() { BuyerId = userId };
var basket = new Basket(userId);
await _basketRepository.AddAsync(basket);

return new BasketViewModel()
Expand Down
10 changes: 2 additions & 8 deletions src/Web/Services/CatalogItemViewModelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,9 @@ public CatalogItemViewModelService(IAsyncRepository<CatalogItem> catalogItemRepo

public async Task UpdateCatalogItem(CatalogItemViewModel viewModel)
{
//Get existing CatalogItem
var existingCatalogItem = await _catalogItemRepository.GetByIdAsync(viewModel.Id);

//Build updated CatalogItem
var updatedCatalogItem = existingCatalogItem;
updatedCatalogItem.Name = viewModel.Name;
updatedCatalogItem.Price = viewModel.Price;

await _catalogItemRepository.UpdateAsync(updatedCatalogItem);
existingCatalogItem.Update(viewModel.Name, viewModel.Price);
await _catalogItemRepository.UpdateAsync(existingCatalogItem);
}
}
}
9 changes: 2 additions & 7 deletions src/Web/Services/CatalogViewModelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,14 @@ public async Task<CatalogIndexViewModel> GetCatalogItems(int pageIndex, int item
// the implementation below using ForEach and Count. We need a List.
var itemsOnPage = await _itemRepository.ListAsync(filterPaginatedSpecification);
var totalItems = await _itemRepository.CountAsync(filterSpecification);

foreach (var itemOnPage in itemsOnPage)
{
itemOnPage.PictureUri = _uriComposer.ComposePicUri(itemOnPage.PictureUri);
}


var vm = new CatalogIndexViewModel()
{
CatalogItems = itemsOnPage.Select(i => new CatalogItemViewModel()
{
Id = i.Id,
Name = i.Name,
PictureUri = i.PictureUri,
PictureUri = _uriComposer.ComposePicUri(i.PictureUri),
Price = i.Price
}),
Brands = await GetBrands(),
Expand Down
Loading

0 comments on commit 3e22803

Please sign in to comment.