Most of the confusion seems to be around functionality that should not exist in the domain model at all:
Persistence should never be in the domain model. Never ever. That's the reason you rely on abstract types such as IRepository
if part of the model ever needs to do something like retrieve a different part of the model, and use dependency injection or some similar technique to wire up the implementation. So strike that from the record.
Authorization is not generally part of your domain model, unless it is actually part of the domain, e.g. if you're writing security software. The mechanics of who is allowed to perform what in an application are normally handled at the "edge" of the business/domain tier, the public parts that the UI and Integration pieces are actually allowed to talk to - the Controller in MVC, the Services or the messaging system itself in an SOA... you get the picture.
Factories (and I assume you mean abstract factories here) aren't exactly bad to have in a domain model but they are almost always unnecessary. Normally you only have a factory when the inner mechanics of object creation might change. But you only have one implementation of the domain model, which means that there will only ever be one kind of factory which always invokes the same constructors and other initialization code.
You can have "convenience" factories if you want - classes that encapsulate common combinations of constructor parameters and so on - but honestly, generally speaking, if you've got a lot of factories sitting in your domain model then you're just wasting lines of code.
So once you turf all of those, that just leaves validation. That's the only one that's kind of tricky.
Validation is part of your domain model but it is also a part of every other component of the application. Your UI and database will have their own, similar yet different validation rules, based on a similar yet different conceptual model. It's not really specified whether or not objects need to have a Validate
method but even if they do, they'll usually delegate it to a validator class (not interface - validation is not abstract in the domain model, it is fundamental).
Keep in mind that the validator is still technically part of the model; it doesn't need to be attached to an aggregate root because it doesn't contain any data or state. Domain models are conceptual things, usually physically translating to an assembly or a collection of assemblies. Don't stress out over the "anemic" issue if your delegation code resides in very close proximity to the object model; it still counts.
What this all really comes down to is that if you're going to do DDD, you have to understand what the domain is. If you're still talking about things like persistence and authorization then you're on the wrong track. The domain represents the running state of a system - the physical and conceptual objects and attributes. Anything that is not directly relevant to the objects and relationships themselves does not belong in the domain model, period.
As a rule of thumb, when considering whether or not something belongs in the domain model, ask yourself the following question:
"Can this functionality ever change for purely technical reasons?" In other words, not due to any observable change to the real-world business or domain?
If the answer is "yes", then it doesn't belong in the domain model. It's not part of the domain.
There's a very good chance that, someday, you'll change your persistence and authorization infrastructures. Therefore, they aren't part of the domain, they're part of the application. This also applies to algorithms, like sorting and searching; you shouldn't go and shove a binary search code implementation into your domain model, because your domain is only concerned with the abstract concept of a search, not how it works.
If, after you've stripped away all the stuff that doesn't matter, you find that the domain model is truly anemic, then that should serve as a pretty good indication that DDD is simply the wrong paradigm for your project.
Some domains really are anemic. Social bookmarking apps don't really have much of a "domain" to speak of; all your objects are basically just data with no functionality. A Sales and CRM system, on the other hand, has a pretty heavy domain; when you load up a Rate
entity then there is a reasonable expectation that you can actually do stuff with that rate, such as apply it to an order quantity and have it figure out the volume discounts and promo codes and all that fun stuff.
Domain objects that just hold data usually do mean that you have an anemic domain model, but that doesn't necessarily mean that you've created a bad design - it might just mean that the domain itself is anemic and that you should be using a different methodology.
Although its a 3 years old thread, still I'm replying it thinking that it might be helpful to somebody.
Table Structure
Table_Offer
--------
ID FK
Name
start time
end time
MandatoryGroup
--------
ProductId (FK to product)
MixGroup1
--------
ProductId (FK to product)
MixGroup2
--------
ProductId (FK to product)
Table_offerDetails
------------------
ID PK
offerId (FK to table_offer)
MandatoryProduct(FK to MandatoryProducts.productID
firstProduct ( fk to MixGroup1.productId)
secondProduct (FK to MixGroup2.productId)
Offer
----
ID name startDate EndDate
--- ---- -------- ------
1 COMBO
MandatoryGroup
-------------
ID name
--- ----
1 A
2 B
MixGroup1
---------
ID name
--- ----
3 P
4 Q
MixGroup2
---------
ID name
--- ----
5 x
6 Y
7 Z
OfferHeader
-------------
ID Customer count(B) B_disc time_disc stat_disc DiscTotal orderPayableTotal
-- --------- ------- ---- -------- --------- -------- ------------
1 BOB 2 20 5 2
OfferDetails
------------
ID offerID MandatoryProduct 1stProduct 2ndProduct
---- ------- --------------- ---------- ----------
1 1 A P X
2 1 A P Y
3 1 A P Z
4 1 A Q X
5 1 A Q Y
6 1 A Q Z
7 1 B P X
8 1 B P Y
9 1 B P Z
10 1 B Q X
11 1 B Q Y
12 1 B Q Z
To construct an offerDetails , you must provide a mandatory , a mixgropu1 and amixGropu2. So caseB , has only 2 offers as follows :
1 A and 2 B, 2 Q, 1 S, 2 X and 1 Z
2B 2Q and 2X =2 offers
1A 1Q and 1X=1st offer
1B 1Q and 1X=2nd Offer ... etc other combinations are vivid.
Other offers are based on business logic:
For quantity offer discount:
create query to find the count of B in an offer for a customer . lets call it QTYB. find QTYB %2 and multiply it with quantityOffer value ( which is $20)
For statistics offer, Just add another bit called earnedPoint for a customer's purchase. and set the earnedPoint an expiry date. earned point can be the same as purchased amount. Check if the purchased amount >=$X , them issue discount as per policy.
Similar is the comparison of order time if its before 5PM and after 10AM then issue 5% else issue 10% discount.
Best Answer
Well, IMHO the point you are missing here is better encapsulation. If you are going to add a new feature to a mega package with 100 functions, you may have to check for side effects or similar things in all those 100 functions (YMMV depending on the actual problem). If you split your package before into 5 smaller packages, you can probably handle such a change more easily, since it may impact a smaller area of your code.
However, when you only seldom change your packages and functions, you have to consider if such a refactoring is worth the trouble - a big refactoring should never be applied just for the sake of itself. And I guess you don't have any refactoring browser for PL/SQL which could assists you (does such a thing exist?), so such a restructuring could mean a lot of work.