Refactored: From Buyer.FullName to Buyer.IdentityGuid.
Also similar changes at the CreateOrderCommand Plus an update to the README.md for GitHub
This commit is contained in:
parent
9f80be5ed9
commit
8325ddbd30
10
README.md
10
README.md
@ -12,7 +12,11 @@ This reference application is cross-platform either in the server and client sid
|
||||
<img src="img/eshop_logo.png">
|
||||
<img src="img/eShopOnContainers_Architecture_Diagram.png">
|
||||
<p>
|
||||
<b>Important Note on Database Servers/Containers</b>: In this solution, the SQL databases are automatically deployed with sample data into a single SQL Server for Linux container (a single shared Docker container for SQL databases) so the whole solution can be up and running without any dependency in the cloud or server. A similar case is defined in regards Redis cache running as a container. However, in a real production environment it is recommended to have persistance (SQL Server and Redis) in HA services like Azure SQL Database, Redis as a service or any other clustering system. If you want to configure this solution like that, you'll just need to change the connection strings once you have set up the servers in the cloud or on-premises.
|
||||
|
||||
> ### Important Note on Database Servers/Containers
|
||||
> In this solution's current configuration for a development environment, the SQL databases are automatically deployed with sample data into a single SQL Server for Linux container (a single shared Docker container for SQL databases) so the whole solution can be up and running without any dependency in the cloud or server. Each database could also be deployed as a single Docker container, but then you'd need more then 8GB or memory RAM in your development machine in order to be able to run 3 SQL Server Docker containers in your Docker Linux host in "Docker for Windows" or "Docker for Mac" development environments.
|
||||
> <p> A similar case is defined in regards Redis cache running as a container for the development environment.
|
||||
> <p> However, in a real production environment it is recommended to have persistance (SQL Server and Redis) in HA services like Azure SQL Database, Redis as a service or any other clustering system. If you want to change to a production configuration, you'll just need to change the connection strings once you have set up the servers in HA cloud or on-premises.
|
||||
|
||||
## Related documentation and guidance
|
||||
While developing this reference application, we've been creating a reference Guide/eBook named <b>"Architecting and Developing Containerized and Microservice based .NET Applications"</b> which explains in detail how to develop this kind of architectural style (microservices, Docker containers, Domain-Driven Design for certain microservices) plus other simpler architectural styles, like monolithic that can also live as Docker containers.
|
||||
@ -67,13 +71,13 @@ The app was also partially tested on "Docker for Mac" using a development MacOS
|
||||
|
||||
WINDOWS DEV MACHINE
|
||||
- Visual Studio 2015 with latest Update
|
||||
- .NET Core 1.0 (Including ASP.NET Core and VS Tooling)
|
||||
- .NET Core 1.1 setup (Including ASP.NET Core and VS Tooling)
|
||||
- Bower and Gulp as global installs (See steps below)
|
||||
- <a href='https://docs.docker.com/docker-for-windows/'>Docker for Windows</a>
|
||||
|
||||
MAC DEV MACHINE
|
||||
- Visual Studio Code
|
||||
- .NET Core 1.0 for Mac
|
||||
- .NET Core 1.1 for Mac - setup
|
||||
- Bower and Gulp as global installs (See steps below)
|
||||
- <a href='https://docs.docker.com/docker-for-mac/'>Docker for Mac</a>
|
||||
|
||||
|
@ -31,7 +31,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
||||
|
||||
public int CardTypeId { get; set; }
|
||||
|
||||
public string BuyerFullName { get; set; }
|
||||
public string BuyerIdentityGuid { get; set; }
|
||||
|
||||
public IEnumerable<OrderItemDTO> OrderItems => _orderItems;
|
||||
|
||||
|
@ -36,11 +36,11 @@
|
||||
// methods and constructor so validations, invariants and business logic
|
||||
// make sure that consistency is preserved across the whole aggregate
|
||||
|
||||
var buyer = await _buyerRepository.FindAsync(message.BuyerFullName);
|
||||
var buyer = await _buyerRepository.FindAsync(message.BuyerIdentityGuid);
|
||||
|
||||
if (buyer == null)
|
||||
{
|
||||
buyer = new Buyer(message.BuyerFullName);
|
||||
buyer = new Buyer(message.BuyerIdentityGuid);
|
||||
}
|
||||
|
||||
var payment = buyer.AddPaymentMethod(message.CardTypeId,
|
||||
|
@ -49,7 +49,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
|
||||
createOrderCommand.CardTypeId = 1;
|
||||
}
|
||||
|
||||
createOrderCommand.BuyerFullName = _identityService.GetUserIdentity();
|
||||
createOrderCommand.BuyerIdentityGuid = _identityService.GetUserIdentity();
|
||||
|
||||
var added = await _mediator.SendAsync(createOrderCommand);
|
||||
if (added)
|
||||
|
@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(OrderingContext))]
|
||||
[Migration("20170208054757_RefactorBuyerWithIdentityGuid")]
|
||||
partial class RefactorBuyerWithIdentityGuid
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752")
|
||||
.HasAnnotation("SqlServer:Sequence:.orderitemseq", "'orderitemseq', '', '1', '10', '', '', 'Int64', 'False'")
|
||||
.HasAnnotation("SqlServer:Sequence:ordering.buyerseq", "'buyerseq', 'ordering', '1', '10', '', '', 'Int64', 'False'")
|
||||
.HasAnnotation("SqlServer:Sequence:ordering.orderseq", "'orderseq', 'ordering', '1', '10', '', '', 'Int64', 'False'")
|
||||
.HasAnnotation("SqlServer:Sequence:ordering.paymentseq", "'paymentseq', 'ordering', '1', '10', '', '', 'Int64', 'False'")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:HiLoSequenceName", "buyerseq")
|
||||
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
|
||||
|
||||
b.Property<string>("IdentityGuid")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IdentityGuid")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("buyers","ordering");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.CardType", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasDefaultValue(1);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("cardtypes","ordering");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:HiLoSequenceName", "paymentseq")
|
||||
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
|
||||
|
||||
b.Property<string>("Alias")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<int>("BuyerId");
|
||||
|
||||
b.Property<string>("CardHolderName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("CardNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(25);
|
||||
|
||||
b.Property<int>("CardTypeId");
|
||||
|
||||
b.Property<DateTime>("Expiration");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("BuyerId");
|
||||
|
||||
b.HasIndex("CardTypeId");
|
||||
|
||||
b.ToTable("paymentmethods","ordering");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:HiLoSequenceName", "orderseq")
|
||||
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
|
||||
|
||||
b.Property<int>("BuyerId");
|
||||
|
||||
b.Property<string>("City")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("Country")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<DateTime>("OrderDate");
|
||||
|
||||
b.Property<int>("OrderStatusId");
|
||||
|
||||
b.Property<int>("PaymentMethodId");
|
||||
|
||||
b.Property<string>("State")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("Street")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("ZipCode")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("BuyerId");
|
||||
|
||||
b.HasIndex("OrderStatusId");
|
||||
|
||||
b.HasIndex("PaymentMethodId");
|
||||
|
||||
b.ToTable("orders","ordering");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:HiLoSequenceName", "orderitemseq")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
|
||||
|
||||
b.Property<decimal>("Discount");
|
||||
|
||||
b.Property<int>("OrderId");
|
||||
|
||||
b.Property<string>("PictureUrl");
|
||||
|
||||
b.Property<int>("ProductId");
|
||||
|
||||
b.Property<string>("ProductName")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<decimal>("UnitPrice");
|
||||
|
||||
b.Property<int>("Units");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OrderId");
|
||||
|
||||
b.ToTable("orderItems","ordering");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderStatus", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasDefaultValue(1);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("orderstatus","ordering");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer")
|
||||
.WithMany("PaymentMethods")
|
||||
.HasForeignKey("BuyerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.CardType", "CardType")
|
||||
.WithMany()
|
||||
.HasForeignKey("CardTypeId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer", "Buyer")
|
||||
.WithMany()
|
||||
.HasForeignKey("BuyerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderStatus", "OrderStatus")
|
||||
.WithMany()
|
||||
.HasForeignKey("OrderStatusId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod", "PaymentMethod")
|
||||
.WithMany()
|
||||
.HasForeignKey("PaymentMethodId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderItem", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order")
|
||||
.WithMany("OrderItems")
|
||||
.HasForeignKey("OrderId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Migrations
|
||||
{
|
||||
public partial class RefactorBuyerWithIdentityGuid : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "FullName",
|
||||
schema: "ordering",
|
||||
table: "buyers",
|
||||
newName: "IdentityGuid");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_buyers_FullName",
|
||||
schema: "ordering",
|
||||
table: "buyers",
|
||||
newName: "IX_buyers_IdentityGuid");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "IdentityGuid",
|
||||
schema: "ordering",
|
||||
table: "buyers",
|
||||
newName: "FullName");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_buyers_IdentityGuid",
|
||||
schema: "ordering",
|
||||
table: "buyers",
|
||||
newName: "IX_buyers_FullName");
|
||||
}
|
||||
}
|
||||
}
|
@ -28,13 +28,13 @@ namespace Ordering.API.Migrations
|
||||
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
|
||||
|
||||
b.Property<string>("FullName")
|
||||
b.Property<string>("IdentityGuid")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("FullName")
|
||||
b.HasIndex("IdentityGuid")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("buyers","ordering");
|
||||
|
@ -8,7 +8,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.B
|
||||
public class Buyer
|
||||
: Entity, IAggregateRoot
|
||||
{
|
||||
public string FullName { get; private set; }
|
||||
public string IdentityGuid { get; private set; }
|
||||
|
||||
private List<PaymentMethod> _paymentMethods;
|
||||
|
||||
@ -23,7 +23,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.B
|
||||
throw new ArgumentNullException(nameof(identity));
|
||||
}
|
||||
|
||||
FullName = identity;
|
||||
IdentityGuid = identity;
|
||||
|
||||
_paymentMethods = new List<PaymentMethod>();
|
||||
}
|
||||
|
@ -47,11 +47,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure
|
||||
buyerConfiguration.Property(b => b.Id)
|
||||
.ForSqlServerUseSequenceHiLo("buyerseq", DEFAULT_SCHEMA);
|
||||
|
||||
buyerConfiguration.Property(b=>b.FullName)
|
||||
buyerConfiguration.Property(b=>b.IdentityGuid)
|
||||
.HasMaxLength(200)
|
||||
.IsRequired();
|
||||
|
||||
buyerConfiguration.HasIndex("FullName")
|
||||
buyerConfiguration.HasIndex("IdentityGuid")
|
||||
.IsUnique(true);
|
||||
|
||||
buyerConfiguration.HasMany(b => b.PaymentMethods)
|
||||
|
@ -49,7 +49,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositor
|
||||
{
|
||||
var buyer = await _context.Buyers
|
||||
.Include(b => b.PaymentMethods)
|
||||
.Where(b => b.FullName == identity)
|
||||
.Where(b => b.IdentityGuid == identity)
|
||||
.SingleOrDefaultAsync();
|
||||
|
||||
return buyer;
|
||||
|
@ -25,7 +25,7 @@ namespace UnitTest.Ordering.Application
|
||||
public async Task Handle_returns_true_when_order_is_persisted_succesfully()
|
||||
{
|
||||
// Arrange
|
||||
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(FakeOrderRequestWithBuyer().BuyerFullName))
|
||||
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(FakeOrderRequestWithBuyer().BuyerIdentityGuid))
|
||||
.Returns(Task.FromResult<Buyer>(FakeBuyer()));
|
||||
|
||||
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken)))
|
||||
@ -48,7 +48,7 @@ namespace UnitTest.Ordering.Application
|
||||
[Fact]
|
||||
public async Task Handle_return_false_if_order_is_not_persisted()
|
||||
{
|
||||
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(FakeOrderRequestWithBuyer().BuyerFullName))
|
||||
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(FakeOrderRequestWithBuyer().BuyerIdentityGuid))
|
||||
.Returns(Task.FromResult<Buyer>(FakeBuyer()));
|
||||
|
||||
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken)))
|
||||
@ -80,7 +80,7 @@ namespace UnitTest.Ordering.Application
|
||||
{
|
||||
return new CreateOrderCommand
|
||||
{
|
||||
BuyerFullName = "1234",
|
||||
BuyerIdentityGuid = "1234",
|
||||
CardNumber = "1234",
|
||||
CardExpiration = DateTime.Now.AddYears(1),
|
||||
CardSecurityNumber = "123",
|
||||
|
Loading…
x
Reference in New Issue
Block a user