tomoco
Senior Member
Đúng. Giống view-model trong M-V-VM. Vm đó chưa có mà cần dùng thì tạo thêm. Tôi thấy ko vấn đề gì cả đâu.
2 bác lại trái ý nhau rồiK cần thừa k sao cả. Bạn có chắc là màn list k thay đổi và sau này nó cần 9/10 field thì sao?
Đúng. Giống view-model trong M-V-VM. Vm đó chưa có mà cần dùng thì tạo thêm. Tôi thấy ko vấn đề gì cả đâu.
2 bác lại trái ý nhau rồiK cần thừa k sao cả. Bạn có chắc là màn list k thay đổi và sau này nó cần 9/10 field thì sao?
Tôi thấy nếu anh làm thế hơi thừa. API list chỉ nên trả về vài fields cần để hiện ra ở list screen thôi. Nếu cần full thông tin thì gọi đến API get by id thì chuẩn hơn.K cần thừa k sao cả. Bạn có chắc là màn list k thay đổi và sau này nó cần 9/10 field thì sao?
Anh có thể thử kiểu này (kể cả client hay server). Tạo ra 1 MinimizedDTO chuyên dùng cho list, tạo ra 1 MaximizedDTO (extends MinimizedDTO) chuyên dùng cho get by ID. Cái MinimizedDTO anh còn có thể vứt vào DTO khác nếu cần show minimized list của thằng này. Nếu anh thấy MinimizedDTO bị thiếu field nào thì move field đấy từ MaximizedDTO sang. Tôi thấy như thế structure khá sáng sủa, dễ maitain và đảm bảo ít bị thừa thông tin.các bác lạc đề quá, câu hỏi của em đơn giản thôiVí dụ: màn hình list user thì chỉ có 2 thông tin: username, email, màn hình user detail thì có full thông tin. Vậy sẽ cần xây dựng 2 model cho từng màn hình?
- phía client.
các bác lạc đề quá, câu hỏi của em đơn giản thôiVí dụ: màn hình list user thì chỉ có 2 thông tin: username, email, màn hình user detail thì có full thông tin. Vậy sẽ cần xây dựng 2 model cho từng màn hình?
- phía client.
Cái này thì nên tách làm interface và implementation riêng.các bác cho hỏi e là việc call đến các hệ thống khác: Ví dụ: AWS S3, MS PowerBI hoặc đơn giản là 1 cái API thời tiết chẳng hạn thì các bác thường để ở đâu nhỉ? Để ở service thì cũng ko đúng lắm vì nó là business logic?

em hiểu cái Bác nói nhưng là để ở đâu nhỉ? service, repository hay ở đâu dc?Cái này thì nên tách làm interface và implementation riêng.
Interface: Storage, BI, Weather -> Cái này là core của mình
Implementation: S3 implement Storage, PowerBI implement BI, AccuWeather implement Weather -> Cái này là infrastructure.
Tách làm 2 để có thể đổi implementation mà không thay đổi nghiệp vụ của core. Còn nếu mà thấy đơn giản quá, không có nhu cầu đổi impl thì thôi khỏi tách.![]()
Service layer hoặc Application layer. Service/Application là nơi glue giữa domain và infrastructure, nó handle cả use-cases.em hiểu cái Bác nói nhưng là để ở đâu nhỉ? service, repository hay ở đâu dc?
em hiểu cái Bác nói nhưng là để ở đâu nhỉ? service, repository hay ở đâu dc?
Service. Nó là external serviceem hiểu cái Bác nói nhưng là để ở đâu nhỉ? service, repository hay ở đâu dc?
/src/Modules/SimplCommerce.Module.Orders/Areas/Orders/Controllers/OrderController.cs đã nói:using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SimplCommerce.Infrastructure.Data;
using SimplCommerce.Module.Core.Extensions;
using SimplCommerce.Module.Core.Services;
using SimplCommerce.Module.Orders.Areas.Orders.ViewModels;
using SimplCommerce.Module.Orders.Models;
namespace SimplCommerce.Module.Orders.Areas.Orders.Controllers
{
[Area("Orders")]
[Authorize]
public class OrderController : Controller
{
private readonly IMediaService _mediaService;
private readonly IRepository<Order> _orderRepository;
private readonly IWorkContext _workContext;
private readonly ICurrencyService _currencyService;
public OrderController(IRepository<Order> orderRepository, IWorkContext workContext, IMediaService mediaService, ICurrencyService currencyService)
{
_orderRepository = orderRepository;
_workContext = workContext;
_mediaService = mediaService;
_currencyService = currencyService;
}
[HttpGet("user/orders")]
public async Task<IActionResult> OrderHistoryList()
{
var user = await _workContext.GetCurrentUser();
var model = await _orderRepository
.Query()
.Where(x => x.CustomerId == user.Id && x.ParentId == null)
.Include(x => x.OrderItems).ThenInclude(x => x.Product).ThenInclude(x => x.ThumbnailImage)
.Include(x => x.OrderItems).ThenInclude(x => x.Product).ThenInclude(x => x.OptionCombinations).ThenInclude(x => x.Option)
.OrderByDescending(x => x.CreatedOn).ToListAsync();
var model2 = model.Select(x => new OrderHistoryListItem(_currencyService)
{
Id = x.Id,
CreatedOn = x.CreatedOn,
SubTotal = x.SubTotal,
OrderStatus = x.OrderStatus,
OrderItems = x.OrderItems.Select(i => new OrderHistoryProductVm
{
ProductId = i.ProductId,
ProductName = i.Product.Name,
Quantity = i.Quantity,
ThumbnailImage = i.Product.ThumbnailImage.FileName,
ProductOptions = i.Product.OptionCombinations.Select(o => o.Value)
}).ToList()
}).ToList();
foreach (var item in model2)
{
foreach (var product in item.OrderItems)
{
product.ThumbnailImage = _mediaService.GetMediaUrl(product.ThumbnailImage);
}
}
return View(model2);
}
[HttpGet("user/orders/{orderId}")]
public async Task<IActionResult> OrderDetails(long orderId)
{
var user = await _workContext.GetCurrentUser();
var order = _orderRepository
.Query()
.Include(x => x.ShippingAddress).ThenInclude(x => x.District)
.Include(x => x.ShippingAddress).ThenInclude(x => x.StateOrProvince)
.Include(x => x.ShippingAddress).ThenInclude(x => x.Country)
.Include(x => x.OrderItems).ThenInclude(x => x.Product).ThenInclude(x => x.ThumbnailImage)
.Include(x => x.OrderItems).ThenInclude(x => x.Product).ThenInclude(x => x.OptionCombinations).ThenInclude(x => x.Option)
.Include(x => x.Customer)
.FirstOrDefault(x => x.Id == orderId);
if (order == null)
{
return NotFound();
}
if (order.CustomerId != user.Id)
{
return BadRequest(new { error = "You don't have permission to view this order" });
}
var model = new OrderDetailVm(_currencyService)
{
Id = order.Id,
IsMasterOrder = order.IsMasterOrder,
CreatedOn = order.CreatedOn,
OrderStatus = (int)order.OrderStatus,
OrderStatusString = order.OrderStatus.ToString(),
CustomerId = order.CustomerId,
CustomerName = order.Customer.FullName,
CustomerEmail = order.Customer.Email,
ShippingMethod = order.ShippingMethod,
PaymentMethod = order.PaymentMethod,
PaymentFeeAmount = order.PaymentFeeAmount,
Subtotal = order.SubTotal,
DiscountAmount = order.DiscountAmount,
SubTotalWithDiscount = order.SubTotalWithDiscount,
TaxAmount = order.TaxAmount,
ShippingAmount = order.ShippingFeeAmount,
OrderTotal = order.OrderTotal,
OrderNote = order.OrderNote,
ShippingAddress = new ShippingAddressVm
{
AddressLine1 = order.ShippingAddress.AddressLine1,
CityName = order.ShippingAddress.City,
ZipCode = order.ShippingAddress.ZipCode,
ContactName = order.ShippingAddress.ContactName,
DistrictName = order.ShippingAddress.District?.Name,
StateOrProvinceName = order.ShippingAddress.StateOrProvince.Name,
Phone = order.ShippingAddress.Phone
},
OrderItems = order.OrderItems.Select(x => new OrderItemVm(_currencyService)
{
Id = x.Id,
ProductId = x.Product.Id,
ProductName = x.Product.Name,
ProductPrice = x.ProductPrice,
Quantity = x.Quantity,
DiscountAmount = x.DiscountAmount,
ProductImage = x.Product.ThumbnailImage.FileName,
TaxAmount = x.TaxAmount,
TaxPercent = x.TaxPercent,
VariationOptions = OrderItemVm.GetVariationOption(x.Product)
}).ToList()
};
foreach (var item in model.OrderItems)
{
item.ProductImage = _mediaService.GetMediaUrl(item.ProductImage);
}
return View(model);
}
}
}
Tôi tìm được 1 repo ở đây viết bằng .Net Core khá giống ý tưởng với bạn thớt -https://github.com/simplcommerce/SimplCommerce, theo đó các xử lý CUD(create/update/delete) đều phải thông qua service, và 1 điểm khác là trong trường hợp query dữ liệu thì dùng repo trực tiếp. Thành thử có vẻ là dung hòa style của bạn thớt và cả cty.
- Tôi chưa thấy chỗ họ implement caching, nhưng nếu làm tôi cho là nên làm ở các Repo.
- Đối với các phức tạp và chạy nặng tôi chủ trương dùng RawSql ở các repo (hi sinh tính linh hoạt switch qua lại giữa các loại db)

em hiểu cái Bác nói nhưng là để ở đâu nhỉ? service, repository hay ở đâu dc?
Lội mãi mới có thím này cùng quan điểm.Ờ, thảm họa như nào vậy bạn? Nó chỉ thảm họa khi rơi vào tay 1 ông lead kém, không biết dẫn dắt team đi theo 1 thể thống nhất ( mà thực ra khi đã kém thì khi áp pattern vào thì nó sẽ ra 1 đống bùi nhùi, chỉ có đập đi làm lại). Chia function thôi cũng là nghệ thuật đấy!.
Tôi ví dụ đơn giản như là windows 32 API , hay linux kernel API nó toàn là 1 đống function đấy, bạn cũng chê nó là thảm họa đúng không?
Mình thì lại thấy maintenance như này dễ hơn do không có vụ "sửa chỗ này hỏng chỗ kia".Từ controller phi thẳng xuống repo thì nó gọi là fat controller nhá, lợi: đơn giản, hại: logic bị phình to ở phía controller, khó tái sử dụng đc chỗ khác, khi nghiệp vụ phức tạp dần thì mantainace cost phi như tên lửa. Nên hay ko nên thì mình k phán ngay đc, phải hiểu project của thớt đã. Mà thôi, k chống lại đc đám đông thì thôi, nhưng nhớ ghi chép lại sự kiện này, đến lúc nào mà có issue sảy ra từ mô hình này thì counter lại mấy thằng ngày xưa phản đối chú, oke?
via theNEXTvoz for iPhone
Lậm quá bạn.Hóng vụ 2 service gọi nhau, các bác xem cách giải quyết của e ok không: Nếu có 1 service cần gọi 1 service khác thì e sẽ tách thành 1 thằng gọi nôm na là usecase, và cả 2 thằng service đều có thể gọi usecase này
Để ở integration layer hoặc repository layer nha bác, integration layer sẽ gọi từ repository layer.các bác cho hỏi e là việc call đến các hệ thống khác: Ví dụ: AWS S3, MS PowerBI hoặc đơn giản là 1 cái API thời tiết chẳng hạn thì các bác thường để ở đâu nhỉ? Để ở service thì cũng ko đúng lắm vì nó là business logic?
Thường như CQRS thì chỉ có code chỗ thằng Handler, gọi thẳng qua repository, handler nó như thằng service luôn.Lậm quá bạn.
Cứ gọi nhau như bình thường, tránh circular dependency là được. Ưu tiên tách khi là code base mới hoặc số lượng method sử dụng chung nhiều chứ đâu nhất thiết phải cứng ngắc không cho gọi nhau.