I am designing a system using domain driven design concepts and I am struggling with a few things. The "domain" is essentially a business system for the company I work for. I am also using dependency injection. So, in my model I have things related to any typical business system (Employee, Order, Invoice, Deposit, etc..). Right now I am trying to create a cash posting application in which users (aka Employees) can create deposits and apply them to unpaid invoices. The problem that I am having is that we are also using an external business system (Microsoft Dynamics Nav) to handle our accounting transactions. So essentially I am dealing with two different databases. So, for the cash posting application I have modeled the domain objects Deposit and DepositLine. I also have in my domain an IDepositRepository interface that is responsible for persisting the deposits. To get a deposit from the system I just want to grab it directly from the database. However, in order to create a deposit I have to use the Dynamics Nav web services because there is certain logic that gets executed behind the scenes that I don't know about. I started looking at the concept of an Anti Corruption layer in which I could translate my version of the deposit object into a deposit object suitable for the web service. So here is what I am envisioning right now:
Domain Layer
- Models
- Deposit
- DepositLine
- Repositories
- IDepositRepository
Infrastructure Layer
- Data
- Repositories
- DepositRepository
- DynamicsNav
- Services
- INavCashManagementService
- Translators
- IDepositTranslator
- Adapters
- INavAdapter
Now I thought i might implement the DepositRepository like so:
public class DepositRepository
{
private INavCashManagementService navCashManagementService;
public DepositRepository(INavCashManagementService navCashManagementService)
{
this.navCashManagementService = navCashManagementService;
}
public Deposit GetDeposit(int id)
{
// use nhibernate to get directly from the database
}
public void SaveDeposit(Deposit deposit)
{
this.navCashManagementService.CreateDeposit(deposit);
}
}
First of all, is this an appropriate design? My next problem is that users are also going to have to "Post" deposits. The Nav web services will also have to be used to run the posting routine. But, this is more of a business process rather than a persistence issue, so I don't see it fitting into the repository. So I am wondering how/where I should call the posting routine. Should I create a domain service like this:
public class CashPostingDomainService
{
private INavCashManagementService navCashManagementService;
public CashPostingDomainService(INavCashManagementService navCashManagementService)
{
this.navCashManagementService = navCashManagementService;
}
public void PostDeposits()
{
this.navCashManagementService.PostDeposits();
}
}
One confusion I have with domain driven design is external dependencies. Doesn't the CashPostingDomainService class now have an external dependency on Nav? I know the implementation isn't in the domain layer, but doesn't the interface itself make it a dependency? The same goes with other technical concerns like sending emails. If I have an IEmailService interface and want to send an email once the deposits are posted, would I inject the interface into the CashPostingDomainService class? Or would that be part of the application workflow? So which one of these options make the most sense (if any):
1
public class DepositController
{
private ICashPostingDomainService cashPostingDomainService;
private IEmailService emailService;
public DepositController(
ICashPostingDomainService cashPostingDomainService,
IEmailService emailService)
{
this.cashPostingDomainService = cashPostingDomainService;
this.emailService = emailService;
}
public void PostDeposits()
{
this.cashPostingDomainService.PostDeposits();
this.emailService.NotifyDepositsPosted();
}
}
2
public class DepositController
{
private ICashPostingDomainService cashPostingDomainService;
public DepositController(
ICashPostingDomainService cashPostingDomainService)
{
this.cashPostingDomainService = cashPostingDomainService;
}
public void PostDeposits()
{
this.cashPostingDomainService.PostDeposits();
}
}
public class CashPostingDomainService
{
private INavCashManagementService navCashManagementService;
private IEmailService emailService;
public CashPostingDomainService(
INavCashManagementService navCashManagementService,
IEmailService emailService)
{
this.navCashManagementService = navCashManagementService;
this.emailService = emailService;
}
public void PostDeposits()
{
this.navCashManagementService.PostDeposits();
this.emailService.NotifyDepositsPosted();
}
}
Thanks for the help!
is this an appropriate design?
It seems fine to me. The important thing is for your Repository to stay oblivious of the Nav side of things and let the anticorruption layer handle that. You might want to have a look here for a similar example.
I know the implementation isn't in the domain layer, but doesn't the
interface itself make it a dependency?
You may have that feeling because the name of your (supposedly agnostic) service interface contains "Nav". To reflect a service abstraction that could have Nav or any other ERP as an implementation, you should rename it to ICashManagementService.
If I have an IEmailService interface and want to send an email once
the deposits are posted, would I inject the interface into the
CashPostingDomainService class? Or would that be part of the
application workflow?
It's your architectural decision to choose one or the other.
Option 1. means that sending an email is an intrinsic part of the deposit posting domain operation. If you take your domain module and reuse it in another application, posting deposits will automatically result in sending an email whatever that application is about. This might be the right thing to do in your context, or you might want to make things a little more generic (like, sending feedback after the operation but not deciding in the domain service whether this feedback should be mail, a log file, etc.)
Option 2. means that the sequence of events that happen after posting the deposits is application specific, that is at the use case level rather than business/domain level. It is up to the Controller (or Application Service) to decide which actions to take -send an email or anything else. Consequently, different applications based around your domain layer could decide to take different actions. This also means possible code duplication between these applications if several of them chose to send mails.
Related
I'm exploring ServiceStack and I'm not sure what is the best way to implement some business logic.
Using the "Bookings CRUD" example I would like to enforce the following rule:
a given Booking can only be saved (either created or updated) if the hotel has enough free rooms for the particular dates of that booking
Please note that I'm not asking how to calculate "free rooms".
What I'm asking is, from the architectural point of view, how should this be done.
For example, one way would be:
create a request DTO to query the number of configured rooms (lets call it "QueryRooms")
use the existing "QueryBookings" to query current bookings present in database
create a " : Service" class to customize the Booking Service, in order to intercept the "CreateBooking" and "UpdateBooking" requests
inside the custom methods for "CreateBooking" and "UpdateBooking", somehow get the results of "QueryRooms" and "QueryBookings", check if there are enough free rooms for the current request, and proceed only if so
This doesn't look very clean, because the service "CreateBooking" and "UpdateBooking" would depend of "QueryRooms" and "QueryBookings".
What would be an elegant and effcient solution, using ServiceStatck?
You can override AutoQuery CRUD operations with your own Service implementation using the AutoQuery DTO.
Where you can use the Service Gateway to call existing Services which you can use to perform any additional validation & modify the request DTO before executing the AutoQuery operation to implement the API, e.g:
public class MyCrudServices : Service
{
public IAutoQueryDb AutoQuery { get; set; }
public object Post(CreateBooking request)
{
var response = Gateway.Send(new QueryRooms
{
From = request.BookingStartDate,
To = request.BookingEndDate,
});
if (response.Results.Count == 0)
throw new Exception("No rooms available during those dates");
request.RoomNumber = response.Results[0].Id;
return AutoQuery.Create(request, base.Request);
}
}
Note: calling in-process Services with the Service Gateway is efficient as it calls the C# method implementation directly, i.e. without incurring any HTTP overhead.
I am thinking about scenario in a way of Domain Driven design, where I have entity, lets say Cv (Curriculum vitae), which state is saved in database via repository.
Now I need to store part of the Cv in another system (ElasticSearch), which is crucial for whole app functionality like searching.
How to handle it? I am thinking about these 2 options:
1. Use domain service IndexCvGatewayServiceInterface (as interfaces implemented in infrastructure)
class CvEntity
{
public function approve(CvRepositoryInterface $cvRepository, IndexCvGatewayServiceInterface $indexService)
{
$cvRepository->update($this);
$indexService->update($this);
}
}
2. Listen to domain event (create infrastructure listener)
class CvEntity
{
public function approve(CvRepositoryInterface $cvRepository, EventDispatcheInterface $dispatcher)
{
$cvRepository->update($this);
$dispatcher->dispatch(new CvApprovedEvent($this));
}
}
I like option 2. because it separates logic for non state change purposes into infrastructure, but there is also concern, that we should know about searching as important part of our app.
You're facing here Write and Read model. Ideally after persist your entity/aggregate in the write model you should dispatch the uncommitted events of this entity and listing/subscribe to them to generate the projections (partials in elastic in your use case). For reference: https://github.com/jorge07/symfony-5-es-cqrs-boilerplate/blob/symfony-5/src/Infrastructure/User/ReadModel/Projections/UserProjectionFactory.php#L17
IMO, Entity should not contain the repository.
I have two aggregates, Advertiser and Payment.
I am using Paypal and Authorize.net payment gateway. So i created interface in infra layer.
interface IPaymentMethod
{
void ProcessPayment(PaymentInfo paymentInfo);
}
and implemented it again in infra layer.
Public class PaypalPaymentGateway : IPaymentMethod
{
public void ProcessPayment(PaymentInfo paymentInfo)
{
// call Paypal api and pass paymentinfo
}
}
//same for authorize.net payment gateway
Below is my app service class
public class PaymentGatewayService : IPaymentGatewayService
{
IPaypalMethod paypalMethod;
public PaymentGatewayService(IPaypalMethod paypalMethod)
{
this.paypalMethod = paypalMethod;
if (paypalMethod == null)
throw new Exception("PaypalMethod not initialized");
}
public void DepositFundInAdvertiser
(PaymentInfo paymentInfo, RegistrationID advertiserRegistrationID)
{
if (paymentMethod != null)
throw new Exception("PaymentMethod empty.");
PaymentResult paymentResult=
paymentMethod.ProcessPayment(paymentInfo);
Advertiser advertiser = advertiserRepository
.Find(advertiserRegistrationID);
advertiser.AddAdvertiserFund(paymentInfo.PaymentTotal);
advertiserRepository.Save(advertiser);
}
}
In App layer - can I inject PaypalMethod interface in App layer constructor and do the following in DepositFundInAdvertiser method?
Remember IPaypalMethod is created and implemented in infra layer.
Your approach seems good enough.
Only I would define IPayer abstraction inside domain layer instead of infrastructure layer.
Because it's (most likely) responsibility of domain model to decide how to react if payment is [un]successful.
//in domain model
public class IPayer{
bool Pay(Money amountToBePaid, BankAccount account);;
}
public class Payment{
public void Pay(IPayer payer){
EnsurePaymentCanBePaid();
IsPaid=payer.Pay(AmountToBePaid,Account);
if(!IsPaid) throw new Exception("Payment failed!");
}
}
//in infrastructure layer
public class PayPalPayer:IPayer{
public bool Pay(Money amountToBePaid, BankAccount account){
//bla bla
}
}
To follow up on my comment. I would also put the PaymentService in infrastructure layer as an infrastructure service. But as Arnis said you should also reflect ho the model respond to this action - Pay.
I (not sure) would probably have some kind of PaymentService in applicaion service layer that works with both entities Advertiser and Payment (through IAdvertiserRepository and IPaymentRepository injected through constructor).
In method (writing this method in memcode):
PaymentService.PayAdvert(Advertiser advertiser, Advert advert, Payment paymentInfo)
{
advertiser.BuyAdvertising(AdvertPayment.Create(advert,paymentInfo))
}
BuyAdvertising method takes both Advert and Payment into AdvertPayment class (which has a static create method for ctor itself. This class can be handy if you want a PaymentHistory collection for Advertiser that contains AdvertPayments in date order. But
BuyAdvertising raises a domain event BuyAdvertisingEvent that triggers a eventhandler in application layer, BuyAdvertisingEventHandler.
This Eventhandler has your IPaypalGatewayService injected in constructor. With the event we have Payment info. If Payment is a success the EventHandler can also have IAdvertiserRepository injected in ctor and save the AdvertPayment into Advertiser payment history collection.
then BuyAdvertising can determine the outcome of payment transaction by checking if advert is paid and added to history collection.
I hope you get the picture. This way you have several tools like an event which is declared in domain model and triggered from the entity advertiser. The model takes action and you have moved out the infra tech logic against PayPal through DomainEventHandler an still, you have not located the logic entirely in application layer. The control is in the domain, but details that domain don't care about is placed in infra layer.
My experience is that these scenarios are common for me and you usually go the easy way, letting the application service do all talking to infralayer. But then you're closingin on a anemic model design, because what is left for the model to do? Just validation properties? Just managing aggregates and collections? I think there is more to it. It should also contains a lot of entity action methods (I call them so... :)) like BuyAdvertisment.
/Cheers
I'm still learning about DDD and I have these two (probably simple) questions:
If a Factory creates new object/graph/aggregate instances, but also "reconstitutes" objects/graphs from the Repository, then:
(1) Does your service layer functions/jobs/tasks/unit-of-work call into the Factory or a behavioural method on the Entity instance or a DomainService function? I'm lost as to the call stack based on the responsibility of these components.
(2) Do Entity instances even have "behavioural methods" like above? For example does a Post have p.UpdatePost(string bodyText) or is that not a concern of the domain model and so the same should be achieved with the Repository? Or the service layer function, should it be calling the Repository in this case and the entity instance simply have behavioural methods that are specific to the domain and not persistence? But then, why does it sound like "updating a post" is a domain function when that's the user's goal?
You can see I'm all over the place. Please help.
(1) Does your service layer functions/jobs/tasks/unit-of-work call into the Factory or a behavioral method on the Entity instance or a DomainService function? I'm lost as to the call stack based on the responsibility of these components.
Usually - top level retrieves necessary aggregate root and calls a function on it. Sometimes top level retrieves multiple aggregate roots and pass them to domain service, but not often because domain service is a quite strong sign that there is unrecognized aggregate root. At the end - top level ensures aggregate root is persisted.
(2) Do Entity instances even have "behavioural methods" like above? For example does a Post have p.UpdatePost(string bodyText) or is that not a concern of the domain model and so the same should be achieved with the Repository? Or the service layer function, should it be calling the Repository in this case and the entity instance simply have behavioural methods that are specific to the domain and not persistence? But then, why does it sound like "updating a post" is a domain function when that's the user's goal?
Yes, they do. Domain model should be aware of it's state changes. And that's much more beneficial as it seems at first. Great thing about this is that You gain extensibility point. If client will walk week later to You and say that he wants system to check additional things when user updates post - instead of searching every line of post.bodyText="new value", You will be able to go straight to post.UpdatePost method and attach necessary things there.
On the other hand - CRUD is not mutually exclusive with domain driven design. E.g. - in my application, management of users and their roles is uninteresting enough that I'm not even trying to model it granularly. You need to recognize parts what matters in business Your application is describing and working with.
Keep in mind that domain driven design makes sense for complex applications only. Simple blog application doesn't need it.
(3) Am I wrong in assuming that a service layer (not Domain Services) should encapsulate how an interface interacts with the Domain Layer?
As I see it - application services are more for orchestrating infrastructure. If there is no infrastructure involved - then application service loses value:
Application services basically are just facades. And every facade is bad if complexity it adds overweights problems it solves.
Inside domain:
//aggregate root is persistence ignorant.
//it shouldn't reference repository directly
public class Customer{
public string Name {get; private set;}
public static Customer Register(string name){
return new Customer(name);
}
protected Customer(string name){
//here it's aware of state changes.
//aggregate root changes it's own state
//instead of having state changed from outside
//through public properties
this.Name=name;
}
}
//domain model contains abstraction of persistence
public interface ICustomerRepository{
void Save(Customer customer);
}
Outside of domain:
public class CustomerRepository:ICustomerRepository{
//here we actually save state of customer into database/cloud/xml/whatever
public void Save(Customer customer){
//note that we do not change state of customer, we just persist it here
_voodoo.StoreItSomehow(customer);
}
}
//asp.net mvc controller
public class CustomerController{
public CustomerController(ICustomerRepository repository){
if (repository==null)throw new ArgumentNullException();
_repository=repository;
}
public ActionResult Register(string name){
var customer=Customer.Register(name);
_repository.Save(customer);
}
}
I'm very glad that i found this website recently, I've learned a lot from here.
I'm from China, and my English is not so good. But i will try to express myself what i want to say.
Recently, I've started learning about Domain Driven Design, and I'm very interested about it. And I plan to develop a Forum website using DDD.
After reading lots of threads from here, I understood that persistence ignorance is a good practice.
Currently, I have two questions about what I'm thinking for a long time.
Should the domain object interact with repository to get/save data?
If the domain object doesn't use repository, then how does the Infrastructure layer (like unit of work) know which domain object is new/modified/removed?
For the second question. There's an example code:
Suppose i have a user class:
public class User
{
public Guid Id { get; set; }
public string UserName { get; set; }
public string NickName { get; set; }
/// <summary>
/// A Roles collection which represents the current user's owned roles.
/// But here i don't want to use the public property to expose it.
/// Instead, i use the below methods to implement.
/// </summary>
//public IList<Role> Roles { get; set; }
private List<Role> roles = new List<Role>();
public IList<Role> GetRoles()
{
return roles;
}
public void AddRole(Role role)
{
roles.Add(role);
}
public void RemoveRole(Role role)
{
roles.Remove(role);
}
}
Based on the above User class, suppose i get an user from the IUserRepository, and add an Role for it.
IUserRepository userRepository;
User user = userRepository.Get(Guid.NewGuid());
user.AddRole(new Role() { Name = "Administrator" });
In this case, i don't know how does the repository or unit of work can know that user has a new role?
I think, a real persistence ignorance ORM framework should support POCO, and any changes occurs on the POCO itself, the persistence framework should know automatically. Even if change the object status through the method(AddRole, RemoveRole) like the above example.
I know a lot of ORM can automatically persistent the changes if i use the Roles property, but sometimes i don't like this way because of the performance reason.
Could anyone give me some ideas for this? Thanks.
This is my first question on this site. I hope my English can be understood.
Any answers will be very appreciated.
Should the domain object interact with repository to get/save data?
No, it should not. Reason for that is simple - encapsulation. If we take away everything persistence related from our domain model, we can describe our domain much clearer.
If the domain object doesn't use repository, then how does the Infrastructure layer (like unit of work) know which domain object is new/modified/removed?
Simplest version is - it doesn't. You retrieve and save it back (after operation on aggregate has completed) as a whole:
var user = users.Find(guid);
user.AssignRole(Role.Administrator);
users.Save(user);
I personally rely on NHibernate - it tracks changes itself. If I optimize queries with proper eager/lazy loading, save changes only on http request end, don't forget about transactions, use caching - there is no performance penalty. But for a price - it takes some knowledge to handle that.
One more thing - think twice before using domain driven design for development of forum. This approach fits only for unknown (yet) and complex business domains. It's an overkill for simple applications.
And another thing - stop being ashamed of Your English. It will get better in no time. :)