Not sure that there is a 'one true way' answer for a design approach that, to be fair, is still evolving. First, DDD and CQRS are not the same thing although the CQRS folks seem to have derived from a DDD-influenced starting point.
There's a lot going on in the DDD mindset and much of it has to do with properly defined boundaries of problems, communication among stakeholders, and interaction between systems, not necessarily a specific implementation in code, so I don't think being too hard-core is a virtue.
You are maybe seeing some of the debate around whether and how domain objects should be changeable, and what function a domain object serves in a system as a whole. CQRS splits a system into read and write pathways, so it's sensible to conclude that you don't actually need read access when you're on the write pathway. Reading then becomes something you do against the events raised by some domain objects and consumed (handled) by others. If you go back a bit in the CQRS history, you'll find arguments that domain objects shouldn't have setters, only getters and a single 'handler' method. The logic here is that only consuming events should result in state change, and that change is entirely handled internally by the domain object.
You show the results of change by treating them as separate artifacts of change, putting them into a separate, flat persistent structure (e.g., a table) and reading it as if you were just reading a report on the current and historical state of the system. For example, you could consume an event by extracting the data you need to read and saving it to a database table that maps closely to a single view (e.g. a screen) of your system.
If you do experiment with this style, be cognizant that other programmers are likely not going to be familiar with this approach, and that there are relatively few (but interesting) scenarios where it's justifiable as a design approach.
For unit testing, there are a couple of approaches that may work for you. The first, and most natural, is to verify the events you expect to see raised by your domain objects are correct. A domain object might raise a generic changed event holding information about the change. You could verify that. You could verify the fact that the event was raised at all, and that other events weren't raised.
Another approach is to use Test Spies that expose readable attributes on your domain object so you can verify state changes. For instance, you can inherit from your domain object, add some accessors to read what would otherwise be encapsulated state and verify that it's correct.
Again, you're confused because these approaches are confusing. If you're looking to adopt some good ideas into your programming, take the juicy bits first. DDD boundaries and making roles explicit are changes to your way of thinking about communicating with your code. CQRS at a minimum suggests that reading data and writing data are segregable operations. That insight leads you to think very clearly about what the role of the data you do need to present is, how much you really need, who's consuming it, how fresh does it need to be, etc... You don't need a full blown Event Sourcing implementation to adopt better encapsulation in your coding. You can start by just focusing on atomic operations within your object, "Tell, Don't Ask" approaches to object interface design, and Inversion of Control through events/handlers rather than strict control within procedural services.
How will encapsulation help when making changes in code and from its
rippling effects. For a data member, if I change its type from int to
float, (even if I am exposing this using property) I will need to
change variable type where I am using already using this code.
The benefit of encapsulation is that it lets you change the internal implementation without breaking client code. It doesn't protect you if you decide that you need to change the interface to your code, but that's a different matter.
Example: Say you have a value representing the price per unit of some commodity. The price is expressed in cents, and because you don't deal in fractional cents you decided to make the property an integer (I'll use C here because I'm not very familiar with C#):
int _price
int pricePerUnit(void) {
return _price;
}
int priceForUnits(int units) {
return units * _price;
}
That all works out fine until one day when somebody notices that your firm is losing a lot of money due to rounding errors. Many of the commodities that you track are bought and sold in lots of many thousands of units, so you need to start tracking the price to an accuracy of at least 0.001 cent. Because you were smart enough to encapsulate the price instead of letting clients access it directly, you can make that change pretty quickly:
double _dprice
int pricePerUnit(void) {
return (int)_dprice;
}
int priceForUnits(int units) {
return (int)(units * _dprice);
}
The interface that clients use to obtain prices stays the same, but the data they get back is now more accurate. If the price per unit is $1.001, priceForUnits(1000000)
will now return a price that's $1000 greater than before. That happens even though you haven't changed the interface to your system at all, and you therefore haven't broken any client code.
Now, that may not always be all that you need to do. Sometimes you'll need to change or augment your interface so that you can report the price more accurately to clients, too:
double pricePerUnit() {
return _dprice;
}
A change like that will break client code, so you might instead keep the old interface and provide a newer, better routine:
int pricePerUnit() {
return (int)_dprice;
}
double accuratePricePerUnit() {
return _dprice;
}
You and the rest of your team can then embark on the process of converting all the clients of your system to use the newer, better accuratePricePerUnit()
. The client code will get more accurate as you make progress on that task, but even the old stuff should continue to work as well as it did in the past.
Anyway, the point is that encapsulation lets you change the way the internals work while presenting a consistent interface, and that helps you make useful changes without breaking other code. It doesn't always protect you from having to update other code, but it can at least help you do that in a controlled manner.
Best Answer
There is a fundamental difference between your Data Access Object (DAO) and your Business Object (BO). Your DAO is a one-to-one mapping of what your data store looks like. In this case, for your
AccountDAO
object, theAccountNo
andBalance
properties will have getters and setters. YourAccount
object - the business layer objects - will not look the same as your DAO.This is just an example, of course, and it is by no means an end-all solution. I know tools like EF have myriad examples where the data object, business object, and view/edit object are all the same object, passed from the database to view. In my experience, that works great on small projects where there is not a lot of business logic. However, it breaks down as your business rules and access restrictions grow in complexity.