Avoiding Getters

1,102 views
Skip to first unread message

Arun Nair

unread,
Oct 7, 2015, 12:06:44 AM10/7/15
to DDD/CQRS
I have been trying to avoid getters in my domain object (primarily to ensure other members of the team do not access the attributes and change t outside the object.  One possible solution is to add a GetSnapshot method to the domain objects, and expose them in that object as read-only attributes.

One other way is to use a visitor pattern and send a delegate to the object, and then use the attributes inside the domain object and pass them to the delegate.

Are there any other ways to ensure this - the primary concern is I want developers to avoid changes to the attributes with the domain object knowing about it, and to preserve immutability of the domain object.

-Arun

Mauro Servienti

unread,
Oct 7, 2015, 12:20:29 AM10/7/15
to ddd...@googlegroups.com
CQRS, the read model is your best friend.
My rule of thumb is: an aggregate has only void methods, behavior, nothing else. The state is fully encapsulated, and hidden, in the aggregate.

.m

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Mauro Servienti

Muhammad Bilal

unread,
Oct 7, 2015, 1:13:09 AM10/7/15
to ddd...@googlegroups.com
Hi,

To make your objects immutable you need to make your setters private and getters public. So you will be having read only attributes outside the class

Thanks,
Muhammad Bilal




Muhammad Bilal  
Senior Software Engineer (.NET), Aurora Solutions

Arun Nair

unread,
Oct 7, 2015, 6:12:05 AM10/7/15
to DDD/CQRS
I am already doing that,, the problem is developers use the getters outside the object in the code, where that code should belong in the object.

Kijana Woodard

unread,
Oct 7, 2015, 10:15:34 AM10/7/15
to ddd...@googlegroups.com
+1 for Mauro's suggestion. They should be relying on events/read models. 
The "domain model" won't have any public properties.

Greg Young

unread,
Oct 7, 2015, 2:09:57 PM10/7/15
to ddd...@googlegroups.com
This is a bit extreme there are times a getter can be pragmatic. The trick is learning where to use them
Studying for the Turing test

Kijana Woodard

unread,
Oct 7, 2015, 2:25:13 PM10/7/15
to ddd...@googlegroups.com
Perhaps there's a terminology issue which is why I put quotes around "domain model". I'm not sure what that means.

The OP mentioned events, so I'm thinking of a "domain model" that is really a set of functions generating events. E.g. a convoluted way to achieve https://gist.github.com/gregoryyoung/a3e69ed58ae066b91f1b via a class. 
[thanks for the gist btw!]

Otherwise, I agree. Be pragmatic. If you're doing c#/java and there's some kind of ORM / document / object hydration going on, go with it. For DTOs, documents, view models, etc, I tend to have getters and setters because it lowers the friction with the rest of the stack. That plus someone abusing a getter/setter for a DTO/view model has never been the root cause of a major issue. 

For the original problem of "developers accessing getters on a domain object", tell them not to do that, explain why, and move on. I wouldn't [much] spend time on a technical solution for a political/social/organizational problem.

Of course, "be pragmatic" !== "do whatever".


Freek Paans

unread,
Oct 7, 2015, 3:32:20 PM10/7/15
to ddd...@googlegroups.com
So what are some cases where you would and wouldn't use them?

@yreynhout

unread,
Oct 7, 2015, 6:21:42 PM10/7/15
to DDD/CQRS
I find, in general, trying to enforce such things through code, a bit of a fallacy. You end up with awkward code because you want to protect developers from themselves. A convention can be communicated verbally and can be enforced through reviews. Using code constructs to achieve the same thing is really telling about the team maturity or the trust instilled upon them. But besides that using getters Is not wrong if the reasons can be justified. For example, you're using an ORM and it's forcing you down that path because you don't want to decouple the domain model and persistence model further. Fine, learn to live with that decision, know why it's there, done. Maybe you want to expose some attributes that are stable thoughout the history of the entity, which in turn might aid lookup of related information in e.g. a domain or application service or make it easy to copy over that information in another collaboration (maybe not always the best of ideas, but I've seen it work). Usually, the shape is a value object. Query methods come to mind as well (e.g. for a computation that doesn't fit the aggregate that maintains/guards the required data). Having to go to a read model for each and every question is plain nonsense when the model could answer that itself. The thing to watch out for is that the write model doesn't get forced into a twisted shape because you have query needs (and I mean the kind asked by the model itself, not for some UI or integration purpose). Walking the rope is indeed a bit of a balancing act. Trying to align querying information with consistency boundaries and time can be challenging and insightful at the same time. There's a school of thinking that everything you need should be in the command. Hm, I find that only holds up to a certain point. Embedding query needs is sometimes a bridge too far, since any change in that part might change the shape of the command, but to each his own. Most of us are building models with eventual consistency anyways (e.g. stuff to input into the system could have been sitting on my desk all morning), so I'm not overly worried about whichever approach is applied. What was the question again?

Ben Kloosterman

unread,
Oct 7, 2015, 6:49:55 PM10/7/15
to ddd...@googlegroups.com
Its hard to say and depends on experience.. 

"Id"  especially is very useful in db stores , caching etc.  Such getters should be non private and exposed via an interface .   

Many optimizations here saving a trip back to the client or read model. Domain generated ( non Guid ) Ids,   Allowing multi aggregate command eg generate order than line items which have some order information. Some simple Transactions fromaccount toaccount etc

None of these are "pure" but can give better  performance and faster to develop, Sagas are expensive to write  and pushing things back to the client can place significant domain coordination logic in the client.  Big commands and generation may be fine in single client but when you have 5 it starts to look a bit bad especially when you need to change the command creation process for a new feature.

A lot of getters can morph it into a DDD type system which is not necessarily bad.


Regards,

Ben

Ben Kloosterman

unread,
Oct 7, 2015, 7:29:22 PM10/7/15
to ddd...@googlegroups.com
I agree with you in principal but it depends .. the current place i'm at does not allow time for code reviews due to a very fast schedule ( and senior management have zero idea about software and are too arrogant to listen , they blew $10M ,mainly to Tata and i threw 80% of there code away as  un-maintainable )  ..by the time you do have time ,  the code is hard to change and your on a maintenance diet budget. 

Also some people don't have the skill / inclination to change eg guys doing 2 tier CRUD for 20 years with stored procs or of the programming "colleges"  in developing countries.   

Obviously the bigger mistake is to try to get these guys to do CQRS , and its important to control the allocation of tasks and put them on the read model  and have some CRUD domains . Now here is the kick most managers doing the task allocation are CRUD/ stored proc guys from 20 years ago.

Sometimes subtle code steers people in the right direction, and keeps the quality higher. The .NET framework has many of these and the writers should be praised. 

I'm sure many people have similar experiences at different jobs but always design for the team ,  if that means you cant introduce some modern concepts wholesale so be it , that's better than the other way around,  The current project i had to make 2 big compromises on a large very distributed system that is synch of data tables not  consistent events and not using event sourcing . To  overcome concurrency they use a field based Db table with nulls that is aggregated,   Neither are ideal but ramming it down peoples throats just mean you end up the "owner" and without ownership you have low quality work.     



Regards,

Ben Kloosterman

Kijana Woodard

unread,
Oct 7, 2015, 7:42:48 PM10/7/15
to ddd...@googlegroups.com
@Ben said - "...he current place i'm at does not allow time for code reviews due to a very fast schedule ( and senior management have zero idea about software and are too arrogant to listen..."

@yreynhout sad - "Using code constructs to achieve the same thing is really telling about the team maturity or the trust instilled upon them."

Ditto organizational failure modes.

--

Arun Nair

unread,
Oct 8, 2015, 4:55:44 AM10/8/15
to DDD/CQRS
I am currently using a DTOs with read-only attributes to expose  state of the domain objects when needed. Plus when the new code has to interact with existing code, I allow getters on those attributes that are needed so that we don't change existing/legacy code. The product does not use any ORMs.  I have tried explaining to people not to use getters arbitrarily, few listen to that, few don't, but when working under tight schedules, they always fall back on getters. The only thing I am aiming for,  is to ensure the domain logic stays inside the domain objects, instead of being scattered around in other places.

-Arun

Ben Kloosterman

unread,
Oct 8, 2015, 6:25:21 AM10/8/15
to ddd...@googlegroups.com
On Thu, Oct 8, 2015 at 7:55 PM, Arun Nair <arun...@gmail.com> wrote:
I am currently using a DTOs with read-only attributes to expose  state of the domain objects when needed. Plus when the new code has to interact with existing code, I allow getters on those attributes that are needed so that we don't change existing/legacy code. The product does not use any ORMs.  I have tried explaining to people not to use getters arbitrarily, few listen to that, few don't, but when working under tight schedules, they always fall back on getters. The only thing I am aiming for,  is to ensure the domain logic stays inside the domain objects, instead of being scattered around in other places.

-Arun


However this means your domain objects are scattered with infrastructure concerns such as Db calls  ( even if via a leaky but nice abstraction such as LINQ). This is one of the main things DDD/CQRS tries to avoid ..and why its devolving. 

I do understand the issue. Some things that may help ( from a C# point of view) 

1. Leave the existing people who are good at CRUD .. on CRUD domains which have little business logic.  Dont change "layers" like business logic change by business domain.  

2,  Get your  programmers who are good at more modern code to work on a CQRS / DDD  domain 

3. Isolate legacy code via interfaces / services.  Move it 1 domain at a time. 

4. Make the database calls and DTOs internal and use internalsvisableto to control which assemblies have access.

5. If you cant do the above use a crap filter layer , which gives better chance to refactor in future.   This is how i altered the existing system ..  Sorry about the quick dump 

Old structure 
UIs 
  Application DLL
    BL DLL ( just grouped CRUD calls) 
       Data DLL ( a lot of logic here) 
           SQL DLL 


New Structure 
UI's
       DataMapper DLL 
                 Data DLL  ( internalsVisableTo   DataMapper)
                 BL DLL ( No dependent dlls except some interfaces) 
                  


        DataMapper has services eg DeliveryService which has CRUD methods  and domain methods eg 

        // CRUD
        public void AllocateToDelivery(Guid deliveryId, int productId, float amount, IEnumerable<Manufactured> materials)
        {
            var mats = materials.ToList();

            // add the product
            mats.Add(new Manufactured()
            {
                DeliveryId = deliveryId,
                ProductId = productId,
                Quantity = amount,
                ModifiedAt = DateTime.UtcNow,
                ModifiedByNode = this.serviceOptions.Node ?? "Set node",
                ManufacturedId = Guid.NewGuid()
            });


            var delivery = blastDeliveryContext.Deliveries.Find(deliveryId);
            MergePumped(mats, delivery);
            blastDeliveryContext.SaveChanges();
        }
 

However when people have time / skill  they can use more pure DDD type where the BL has no data infrastructure 

  public void ApproveDelivery(Guid deliveryId)
    {
            var changedEntity = MapBusinessToEFEntity(deliveryId, x => x.ApproveDelivery());

    }

  // used by many 
  DeliveryAggregate MapBusinessToEFEntity(Guid deliveryId, Action<DeliveryLogic> deliveryAction)
   {
            var deliveryEntity = GetDelivery(deliveryId);
            var delivery = new DeliveryLogic(deliveryEntity);  // business domain. 

            deliveryAction.Invoke(delivery);
            delivery.UpdateEntityTree(deliveryEntity);
            dbConverter.UpdateDeliveryAndSave(deliveryEntity);

            return deliveryEntity;
   }



and the business logic is contained ,easy to understand and pure  ( some have a lot more BL but is still very clean and readable) , with no setters and getters ( note the entity your DTO is passed in)
    public class DeliveryLogic
    {

        public void ApproveDelivery()
        {
            if ( status == DeliveryStatuses.Completed 
                || status == DeliveryStatuses.ReceivedBySAP
                || status == DeliveryStatuses.Cancelled
                )
                throw new ApplicationException("Cant approve as completed or cancelled");
           
            status = DeliveryStatuses.Approved;
        }

Note BL has no access to data and this is enforced with a test with explanation.

Its not ideal but it does give better options for the future. 

Regards,

Ben 



Arun Nair

unread,
Oct 8, 2015, 7:48:34 AM10/8/15
to DDD/CQRS
Thanks for the detailed example - just to be clear the domain objects do not depend on infrastructure code, nor repositories,only some interfaces. The 'internalsVisibleto' is a good idea, we do use it for unit tests only for now, but I  like your suggestion.

Kijana Woodard

unread,
Oct 8, 2015, 9:34:37 AM10/8/15
to ddd...@googlegroups.com
Ha. When you said "use getters", I was thinking you didn't want people *calling* get properties, but it seems you're trying to avoid people "writing" get properties.

Ultimately this is a staffing/training problem. It's a mistake to write "a mountain of code" to solve it. Who's going to maintain that? Plus, the friction to adding new code "the right way" means people will fall back to habits, as you say.

Another solution to this problem is to assign the "CRUD oriented developers" to the read side, which is [could be] simple crud. Call this sproc / query this table and build this view.

Peter Hageus

unread,
Oct 8, 2015, 10:21:14 AM10/8/15
to ddd...@googlegroups.com
I think you can write a pretty simple unittest using reflection (or NDepend or similar), making sure there are nog public properties on a certain group of classes. But it does seems like a people problem first and foremost… I’d start there.

/Peter

Ben Kloosterman

unread,
Oct 8, 2015, 8:13:14 PM10/8/15
to ddd...@googlegroups.com
 Note i consider injecting the IDAL , IRepositry Data access interfaces (or lazy loading with a proxy object) into business objects as still exposing infrastructure to business objects , im harping on this not because you may or may not have used it but because that is normally the cause of devolving back to CRUD style especially with CRUD developers. 

No infrastructure means no Save , Load  or lazy loading calls in the object not just interfaces. This is not trivial to achieve when converting older style apps .. and CQRS / Event sourcing is one of the ways.  In the project i posted the DataMappers have this code which is why the CRUD code is there  , which raised a few questions around "why is it called a datamapper not BL ?".   Where are the "business objects" and by that they mean the anemic  CRUD style objects( which are more groupings )  with an IDAL injected.  


Regarding People issues its easy to say - its a people issue but how do you solve that .. perhaps another thread.. Its not just this contract but a lot of the other large ones as well ( though merchant bank jobs i did were anti CRUD)  . If you can hire alphas / hobbyists that's fine but when you arrive and there is 10+ senior guys in the team over 50 with 20 years+ experience and a relaxed attitude and your job is to use them than its very tricky ..  I still stand by design the solution to the team not the other way around. 

Obviously smaller teams are easier ti get the right dynamics.

You shouldn't have to write a mountain of code to solve that , a few lines or apis can steer people subtlety in the right direction eg internalsvisableto  or the .net/java/python etc  immutable string apis

Ben

Arun Nair

unread,
Oct 9, 2015, 4:02:45 AM10/9/15
to DDD/CQRS
One solution presented in the book Patterns Principles and Practices of DDD (http://www.amazon.in/Patterns-Principles-Practices-Domain-Driven-Design/dp/1118714709) to avoiding getters, is to use the Memento pattern.
public class Basket
{
private Basket(BasketSnapshot snapshot)
{
Items = CreateBasketItemsFrom(snapshot.ItemsSnapshot);
DeliveryCost = snapshot.DeliveryCost;
}
// ...
public BasketSnapshot GetSnapshot()
{
var snapshot = new BasketSnapshot();
snapshot.ItemsSnapshot = CreateItemSnapshotFrom(Items);
snapshot.DeliveryCost = this.DeliveryCost;
return snapshot;
}
public static void CreateBasketFrom(BasketSnapshot snapshot)
{
return new Basket(snapshot);


public class BasketRepository : BasketRepository
{
// ....
public Basket FindBy(Guid id)
{
Basket basket;
BasketDataModel
basketDataModel = FindDataModelBy(id);
if (basketDataModel != null)
{
var basketSnapshot = ConvertToBasketSnshot(basketDataModel);
basket = Basket.CreateBasketFrom(basketSnapshot);
}
return basket;
.......
.....
.....

Arun 

Jens Rantil

unread,
Nov 18, 2015, 5:50:24 PM11/18/15
to DDD/CQRS, mbi...@aurorasolutions.io
I stumbled across http://immutables.github.io the other day. Looks really interesting for introducing immutability within the Java sphere. I'm sure there are similar libraries for other libraries. Highly useful for value objects, too.

Cheers,
Jens
Reply all
Reply to author
Forward
0 new messages