Browse Source

Finished first iteration over ordering.data

pull/49/merge
Unai 8 years ago
parent
commit
fb8294a5c5
20 changed files with 423 additions and 81 deletions
  1. +0
    -15
      src/Services/Ordering/Ordering.Application/Commands/CancelOrderRequest.cs
  2. +0
    -15
      src/Services/Ordering/Ordering.Application/Commands/CancelOrderRequestHandler.cs
  3. +97
    -2
      src/Services/Ordering/Ordering.Application/Commands/NewOrderREquestHandler.cs
  4. +23
    -0
      src/Services/Ordering/Ordering.Application/Commands/NewOrderRequest.cs
  5. +34
    -0
      src/Services/Ordering/Ordering.Application/Decorators/LogDecorator.cs
  6. +3
    -1
      src/Services/Ordering/Ordering.Application/Queries/IOrderQueries.cs
  7. +19
    -2
      src/Services/Ordering/Ordering.Application/Queries/OrderQueries.cs
  8. +3
    -1
      src/Services/Ordering/Ordering.Application/project.json
  9. +5
    -14
      src/Services/Ordering/Ordering.Domain/Address.cs
  10. +15
    -0
      src/Services/Ordering/Ordering.Domain/Buyer.cs
  11. +2
    -2
      src/Services/Ordering/Ordering.Domain/CardType.cs
  12. +19
    -5
      src/Services/Ordering/Ordering.Domain/Order.cs
  13. +3
    -5
      src/Services/Ordering/Ordering.Domain/OrderStatus.cs
  14. +32
    -4
      src/Services/Ordering/Ordering.Domain/Payment.cs
  15. +13
    -0
      src/Services/Ordering/Ordering.Domain/Repositories/IBuyerRepository.cs
  16. +10
    -0
      src/Services/Ordering/Ordering.Domain/Repositories/IOrderRepository.cs
  17. +2
    -1
      src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs
  18. +56
    -14
      src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs
  19. +51
    -0
      src/Services/Ordering/Ordering.Infrastructure/Repositories/BuyerRepository.cs
  20. +36
    -0
      src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs

+ 0
- 15
src/Services/Ordering/Ordering.Application/Commands/CancelOrderRequest.cs View File

@ -1,15 +0,0 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Application.Commands
{
using MediatR;
public class CancelOrderRequest
: IAsyncRequest<bool>
{
public int OrderId { get; private set; }
public CancelOrderRequest(int orderId)
{
OrderId = orderId;
}
}
}

+ 0
- 15
src/Services/Ordering/Ordering.Application/Commands/CancelOrderRequestHandler.cs View File

@ -1,15 +0,0 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Application.Commands
{
using MediatR;
using System;
using System.Threading.Tasks;
public class CancelOrderRequestHandler
: IAsyncRequestHandler<CancelOrderRequest, bool>
{
public Task<bool> Handle(CancelOrderRequest message)
{
throw new NotImplementedException();
}
}
}

+ 97
- 2
src/Services/Ordering/Ordering.Application/Commands/NewOrderREquestHandler.cs View File

@ -1,15 +1,110 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Application.Commands
{
using Domain.Repositories;
using MediatR;
using System.Linq;
using System;
using System.Threading.Tasks;
using Domain;
public class NewOrderRequestHandler
: IAsyncRequestHandler<NewOrderRequest, bool>
{
public Task<bool> Handle(NewOrderRequest message)
private readonly IBuyerRepository _buyerRepository;
private readonly IOrderRepository _orderRepository;
public NewOrderRequestHandler(IBuyerRepository buyerRepository,IOrderRepository orderRepository)
{
if (buyerRepository == null)
{
throw new ArgumentNullException(nameof(buyerRepository));
}
if (orderRepository == null)
{
throw new ArgumentNullException(nameof(orderRepository));
}
_buyerRepository = buyerRepository;
_orderRepository = orderRepository;
}
public async Task<bool> Handle(NewOrderRequest message)
{
//find buyer/payment or add a new one
var buyer = await _buyerRepository.FindAsync(message.Buyer);
if (buyer == null)
{
buyer = CreateBuyer(message);
}
var payment = GetExistingPaymentOrAddANewOne(buyer, message);
await _buyerRepository.UnitOfWork.SaveChangesAsync();
//create order
var order = CreateOrder(buyer.Id, payment.Id, 0);
order.SetAddress( new Address()
{
City = message.City,
State = message.State,
Street = message.Street,
ZipCode = message.ZipCode
});
_orderRepository.Add(order);
var result = await _orderRepository.UnitOfWork.SaveChangesAsync();
return result > 0;
}
Payment GetExistingPaymentOrAddANewOne(Buyer buyer, NewOrderRequest message)
{
Payment payment = PaymentAlreadyExist(buyer, message);
if (payment == null)
{
payment = CreatePayment(message);
buyer.Payments.Add(payment);
}
return payment;
}
Payment PaymentAlreadyExist(Domain.Buyer buyer, NewOrderRequest message)
{
return buyer.Payments
.SingleOrDefault(p =>
{
return p.CardHolderName == message.CardHolderName
&&
p.CardNumber == message.CardNumber
&&
p.Expiration == message.CardExpiration
&&
p.SecurityNumber == message.CardSecurityNumber;
});
}
Buyer CreateBuyer(NewOrderRequest message)
{
return _buyerRepository.Add(
new Buyer(message.Buyer));
}
Order CreateOrder(int buyerId, int paymentId, int addressId)
{
return new Order(buyerId, paymentId);
}
Payment CreatePayment(NewOrderRequest message)
{
throw new NotImplementedException();
return new Payment(message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration, message.CardTypeId);
}
}
}

+ 23
- 0
src/Services/Ordering/Ordering.Application/Commands/NewOrderRequest.cs View File

@ -1,10 +1,33 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Application.Commands
{
using System;
using MediatR;
public class NewOrderRequest
:IAsyncRequest<bool>
{
public string City { get; set; }
public string Street { get; set; }
public string State { get; set; }
public string Country { get; set; }
public string ZipCode { get; set; }
public string CardNumber { get; set; }
public string CardHolderName { get; set; }
public DateTime CardExpiration { get; set; }
public string CardSecurityNumber { get; set; }
public int CardTypeId { get; set; }
public string Buyer { get; set; }
public NewOrderRequest()
{
}


+ 34
- 0
src/Services/Ordering/Ordering.Application/Decorators/LogDecorator.cs View File

@ -0,0 +1,34 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Application.Decorators
{
using Extensions.Logging;
using MediatR;
using System.Threading.Tasks;
public class LogDecorator<TRequest, TResponse>
: IAsyncRequestHandler<TRequest, TResponse>
where TRequest : IAsyncRequest<TResponse>
{
private readonly IAsyncRequestHandler<TRequest, TResponse> _inner;
private readonly ILogger<LogDecorator<TRequest, TResponse>> _logger;
public LogDecorator(
IAsyncRequestHandler<TRequest, TResponse> inner,
ILogger<LogDecorator<TRequest, TResponse>> logger)
{
_inner = inner;
_logger = logger;
}
public async Task<TResponse> Handle(TRequest message)
{
_logger.LogInformation($"Executing command {_inner.GetType().FullName}");
var response = await _inner.Handle(message);
_logger.LogInformation($"Succedded executed command {_inner.GetType().FullName}");
return response;
}
}
}

+ 3
- 1
src/Services/Ordering/Ordering.Application/Queries/IOrderQueries.cs View File

@ -6,6 +6,8 @@
{
Task<dynamic> GetOrder(int id);
Task<dynamic> GetPendingOrders();
Task<dynamic> GetOrders();
Task<dynamic> GetCardTypes();
}
}

+ 19
- 2
src/Services/Ordering/Ordering.Application/Queries/OrderQueries.cs View File

@ -4,6 +4,7 @@
using Microsoft.Extensions.Configuration;
using System.Data.SqlClient;
using System.Threading.Tasks;
using System;
public class OrderQueries
:IOrderQueries
@ -15,6 +16,7 @@
_connectionString = configuration["ConnectionString"];
}
public async Task<dynamic> GetOrder(int id)
{
using (var connection = new SqlConnection(_connectionString))
@ -25,14 +27,29 @@
}
}
public async Task<dynamic> GetPendingOrders()
public async Task<dynamic> GetOrders()
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
return await connection.QueryAsync<dynamic>("SELECT * FROM ordering.Orders");
return await connection.QueryAsync<dynamic>(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status],SUM(oi.units*oi.unitprice) as total
FROM [ordering].[Orders] o
LEFT JOIN[ordering].[orderitems] oi ON o.Id = oi.orderid
LEFT JOIN[ordering].[orderstatus] os on o.StatusId = os.Id
GROUP BY o.[Id], o.[OrderDate], os.[Name]");
}
}
public async Task<dynamic> GetCardTypes()
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
return await connection.QueryAsync<dynamic>("SELECT * FROM ordering.cardtypes");
}
}
}
}

+ 3
- 1
src/Services/Ordering/Ordering.Application/project.json View File

@ -8,7 +8,9 @@
"System.Dynamic.Runtime": "4.0.11",
"Microsoft.CSharp": "4.0.1",
"Microsoft.Extensions.Configuration": "1.0.0",
"System.Data.SqlClient": "4.1.0"
"System.Data.SqlClient": "4.1.0",
"Microsoft.Extensions.Logging.Abstractions": "1.0.0",
"Ordering.Domain": "1.0.0-*"
},
"frameworks": {


+ 5
- 14
src/Services/Ordering/Ordering.Domain/Address.cs View File

@ -6,24 +6,15 @@
public class Address
: Entity
{
public String Street { get; private set; }
public String Street { get; set; }
public String City { get; private set; }
public String City { get; set; }
public String State { get; private set; }
public String State { get; set; }
public String StateCode { get; private set; }
public String Country { get; set; }
public String Country { get; private set; }
public String ZipCode { get; set; }
public String CountryCode { get; private set; }
public String ZipCode { get; private set; }
public double Latitude { get; private set; }
public double Longitude { get; private set; }
protected Address() { }
}
}

+ 15
- 0
src/Services/Ordering/Ordering.Domain/Buyer.cs View File

@ -1,12 +1,27 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain
{
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
using System;
using System.Collections.Generic;
public class Buyer
:Entity,IAggregateRoot
{
public string FullName { get; private set; }
public HashSet<Payment> Payments { get; private set; }
protected Buyer() { }
public Buyer(string fullName)
{
if (String.IsNullOrWhiteSpace(fullName))
{
throw new ArgumentNullException(nameof(fullName));
}
this.FullName = fullName;
this.Payments = new HashSet<Payment>();
}
}
}

+ 2
- 2
src/Services/Ordering/Ordering.Domain/CardType.cs View File

@ -11,8 +11,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain
: Entity
{
public static CardType Amex = new CardType(1, "Amex");
public static CardType Visa = new CardType(1, "Visa");
public static CardType MasterCard = new CardType(1, "MasterCard");
public static CardType Visa = new CardType(2, "Visa");
public static CardType MasterCard = new CardType(3, "MasterCard");
public string Name { get; private set; }


+ 19
- 5
src/Services/Ordering/Ordering.Domain/Order.cs View File

@ -5,7 +5,7 @@
using System.Collections.Generic;
public class Order
:Entity, IAggregateRoot
: Entity, IAggregateRoot
{
public int BuyerId { get; private set; }
@ -23,14 +23,28 @@
public Address ShippingAddress { get; private set; }
public int? BillingAddressId { get; private set; }
public Address BillingAddress { get; private set; }
public int PaymentId { get; private set; }
public Payment Payment { get; private set; }
protected Order() { }
public Order(int buyerId, int paymentId)
{
BuyerId = buyerId;
PaymentId = paymentId;
StatusId = OrderStatus.InProcess.Id;
OrderDate = DateTime.UtcNow;
}
public void SetAddress(Address address)
{
if (address == null)
{
throw new ArgumentNullException(nameof(address));
}
ShippingAddress = address;
}
}
}

+ 3
- 5
src/Services/Ordering/Ordering.Domain/OrderStatus.cs View File

@ -10,11 +10,9 @@
{
public string Name { get; private set; }
public static OrderStatus New = new OrderStatus(1, nameof(New).ToLowerInvariant());
public static OrderStatus Submitted = new OrderStatus(1, nameof(Submitted).ToLowerInvariant());
public static OrderStatus InProcess = new OrderStatus(1, nameof(InProcess).ToLowerInvariant());
public static OrderStatus Shipped = new OrderStatus(1, nameof(Shipped).ToLowerInvariant());
public static OrderStatus Canceled = new OrderStatus(1, nameof(Canceled).ToLowerInvariant());
public static OrderStatus Shipped = new OrderStatus(2, nameof(Shipped).ToLowerInvariant());
public static OrderStatus Canceled = new OrderStatus(3, nameof(Canceled).ToLowerInvariant());
protected OrderStatus()
{
@ -28,7 +26,7 @@
public static IEnumerable<OrderStatus> List()
{
return new[] { New, Submitted, InProcess, Shipped, Canceled };
return new[] { InProcess, Shipped, Canceled };
}
public static OrderStatus FromName(string name)


+ 32
- 4
src/Services/Ordering/Ordering.Domain/Payment.cs View File

@ -2,14 +2,13 @@
{
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class Payment
: Entity, IAggregateRoot
: Entity
{
public int BuyerId { get; private set; }
public string CardNumber { get; private set; }
public string SecurityNumber { get; private set; }
@ -23,5 +22,34 @@
public DateTime Expiration { get; private set; }
protected Payment() { }
public Payment(string cardNumber, string securityNumber, string cardHolderName, DateTime expiration, int cardTypeId)
{
if (String.IsNullOrWhiteSpace(cardNumber))
{
throw new ArgumentException(nameof(cardNumber));
}
if (String.IsNullOrWhiteSpace(securityNumber))
{
throw new ArgumentException(nameof(securityNumber));
}
if (String.IsNullOrWhiteSpace(cardHolderName))
{
throw new ArgumentException(nameof(cardHolderName));
}
if (expiration < DateTime.UtcNow)
{
throw new ArgumentException(nameof(expiration));
}
this.CardNumber = cardNumber;
this.SecurityNumber = securityNumber;
this.CardHolderName = cardHolderName;
this.Expiration = expiration;
this.CardTypeId = cardTypeId;
}
}
}

+ 13
- 0
src/Services/Ordering/Ordering.Domain/Repositories/IBuyerRepository.cs View File

@ -0,0 +1,13 @@
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Repositories
{
public interface IBuyerRepository
:IRepository
{
Buyer Add(Buyer buyer);
Task<Buyer> FindAsync(string name);
}
}

+ 10
- 0
src/Services/Ordering/Ordering.Domain/Repositories/IOrderRepository.cs View File

@ -0,0 +1,10 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Repositories
{
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
public interface IOrderRepository
:IRepository
{
Order Add(Order order);
}
}

+ 2
- 1
src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs View File

@ -1,10 +1,11 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
{
public interface IUnitOfWork : IDisposable
{
Task<int> CommitAsync();
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
}
}

+ 56
- 14
src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs View File

@ -1,11 +1,15 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure
{
using System;
using System.Threading.Tasks;
using Domain.SeedWork;
using EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.eShopOnContainers.Services.Ordering.Domain;
public class OrderingContext
: DbContext
: DbContext,IUnitOfWork
{
const string DEFAULT_SCHEMA = "ordering";
@ -18,7 +22,7 @@
public DbSet<Buyer> Buyers { get; set; }
public DbSet<CardType> Cards { get; set; }
public DbSet<CardType> CardTypes { get; set; }
public DbSet<OrderStatus> OrderStatus { get; set; }
@ -32,21 +36,23 @@
modelBuilder.Entity<Payment>(ConfigurePayment);
modelBuilder.Entity<Order>(ConfigureOrder);
modelBuilder.Entity<OrderItem>(ConfigureOrderItems);
modelBuilder.Entity<OrderStatus>()
.ToTable("orderstatus", DEFAULT_SCHEMA);
modelBuilder.Entity<CardType>()
.ToTable("cardtypes", DEFAULT_SCHEMA);
modelBuilder.Entity<CardType>(ConfigureCardTypes);
modelBuilder.Entity<OrderStatus>(ConfigureOrderStatus);
modelBuilder.Entity<Address>(ConfigureAddress);
modelBuilder.Entity<Address>()
.ToTable("address", DEFAULT_SCHEMA);
}
void ConfigureBuyer(EntityTypeBuilder<Buyer> buyerConfiguration)
{
buyerConfiguration.ToTable("buyers", DEFAULT_SCHEMA);
buyerConfiguration.HasIndex(b => b.FullName)
.IsUnique(true);
buyerConfiguration.HasKey(b => b.Id);
buyerConfiguration.Property(b => b.Id)
@ -55,6 +61,11 @@
buyerConfiguration.Property(b => b.FullName)
.HasMaxLength(200)
.IsRequired();
buyerConfiguration.HasMany(b => b.Payments)
.WithOne()
.HasForeignKey(p => p.BuyerId)
.OnDelete(DeleteBehavior.Cascade);
}
void ConfigurePayment(EntityTypeBuilder<Payment> paymentConfiguration)
@ -96,12 +107,8 @@
orderConfiguration.HasOne(o => o.Payment)
.WithMany()
.HasForeignKey(o => o.PaymentId);
orderConfiguration.HasOne(o => o.BillingAddress)
.WithMany()
.HasForeignKey(o => o.BillingAddressId)
.OnDelete(EntityFrameworkCore.Metadata.DeleteBehavior.SetNull);
.HasForeignKey(o => o.PaymentId)
.OnDelete(DeleteBehavior.Restrict);
orderConfiguration.HasOne(o => o.Buyer)
.WithMany()
@ -134,5 +141,40 @@
.ForSqlServerHasDefaultValue(1)
.IsRequired();
}
void ConfigureOrderStatus(EntityTypeBuilder<OrderStatus> orderStatusConfiguration)
{
orderStatusConfiguration.ToTable("orderstatus", DEFAULT_SCHEMA);
orderStatusConfiguration.HasKey(o => o.Id);
orderStatusConfiguration.Property(o => o.Id)
.HasDefaultValue(1)
.IsRequired();
orderStatusConfiguration.Property(o => o.Name)
.HasMaxLength(200)
.IsRequired();
}
void ConfigureCardTypes(EntityTypeBuilder<CardType> cardTypesConfiguration)
{
cardTypesConfiguration.ToTable("cardtypes", DEFAULT_SCHEMA);
cardTypesConfiguration.HasKey(ct => ct.Id);
cardTypesConfiguration.Property(ct => ct.Id)
.HasDefaultValue(1)
.IsRequired();
cardTypesConfiguration.Property(ct => ct.Name)
.HasMaxLength(200)
.IsRequired();
}
void ConfigureAddress(EntityTypeBuilder<Address> addressConfiguration)
{
addressConfiguration.ToTable("address", DEFAULT_SCHEMA);
}
}
}

+ 51
- 0
src/Services/Ordering/Ordering.Infrastructure/Repositories/BuyerRepository.cs View File

@ -0,0 +1,51 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories
{
using Domain.SeedWork;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Ordering.Domain;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Repositories;
using System;
using System.Linq;
using System.Threading.Tasks;
public class BuyerRepository
: IBuyerRepository
{
private readonly OrderingContext _context;
public BuyerRepository(OrderingContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
_context = context;
}
public IUnitOfWork UnitOfWork
{
get
{
return _context;
}
}
public Buyer Add(Buyer buyer)
{
return _context.Buyers
.Add(buyer)
.Entity;
}
public async Task<Buyer> FindAsync(string name)
{
var buyer = await _context.Buyers
.Include(b => b.Payments)
.Where(b => b.FullName == name)
.SingleOrDefaultAsync();
return buyer;
}
}
}

+ 36
- 0
src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs View File

@ -0,0 +1,36 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories
{
using Domain;
using Domain.SeedWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Repositories;
using System;
public class OrderRepository
: IOrderRepository
{
private readonly OrderingContext _context;
public IUnitOfWork UnitOfWork
{
get
{
return _context;
}
}
public OrderRepository(OrderingContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
_context = context;
}
public Order Add(Order order)
{
return _context.Orders.Add(order)
.Entity;
}
}
}

Loading…
Cancel
Save