Jump to content

Favor traits with interfaces over inheritance


NotionCommotion

Recommended Posts

Many will say "favor composition over inheritance".  Others will say "Use inheritance when you want to add functionality and use composition when you want to modify functionality.  Both seem like sound advice.

My current predicament doesn't completely fit but hoping it does enough.  Working on a multitenant application where most entities must belong to a given Tenant, so I create the AbstractEntityBelongsToTenant class who's sole purpose is to add the Tenant to the entities and extend all the other classes from it.  I then have listeners which look for AbstractEntityBelongsToTenant and filter, enforce, etc.

Turns out I have two types of users: TenantUser which belongs to a given tenant and VendorUser which belongs to a given Vendor which in turn belongs to a given Tenant.  So, I create AbstractUser which has properties such as name, email, etc, make it extend AbstractEntityBelongsToTenant, and make both TenantUser and VendorUser extend AbstractUser.

All good!  But then I realize I need a SuperUser which also has properties such as name, email, etc, but doesn't belong to a single Tenant.  Hummm...

When inheritance doesn't diverge, I think it is simple and easy to understand, and while this scenario isn't real common, it more common than I would like to admit.  I am thinking for this case to create EntityBelongsToTenantTrait and make TenantUser and VendorUser use it, and then make both those classes implement EntityBelongsToTenantInterface.  Suppose I could make UserTrait but don't think it is as obvious.

I am not saying one should favor traits with interfaces over inheritance all the time, but am asking whether one shouldn't hesitate to use traits with interfaces when this issue comes up and composition doesn't solve the issue and if there are any/many negative implications of doing so which I might be unaware of.

 

Thanks

Link to comment
Share on other sites

It's a subtle distinction but don't let yourself fall into the trap of thinking traits constitute composition. Because they don't. Traits are only code reuse. So the question is whether "language-assisted copy-and-paste" is the solution to the problem.

And sometimes it is. But I don't think this is one of those times.

The way I see it, it isn't so much about a hierarchy but about a relationship: a "tenant" user is associated with a tenant, a "vendor" user is associated with a vendor (which is associated with a tenant), and a "super" user is associated with zero(?)-or-more tenants.

What's your UML for the users? In other words, what kinds of properties do the different types of users have, and thus how are they different from each other?

Link to comment
Share on other sites

19 hours ago, requinix said:

It's a subtle distinction but don't let yourself fall into the trap of thinking traits constitute composition. Because they don't. Traits are only code reuse. So the question is whether "language-assisted copy-and-paste" is the solution to the problem.

Thank you but no I don't see them as such but only as a not so elegant technique which could provide elegant solutions at times as long as interfaces are used (at least this is my latest revelation).

19 hours ago, requinix said:

and a "super" user is associated with zero(?)-or-more tenants.

Zero.  Or all.  They are not associated with any given tenant and have access to all tenants.
 

19 hours ago, requinix said:

What's your UML for the users? In other words, what kinds of properties do the different types of users have, and thus how are they different from each other?

  1. Most significant is who they are directly associated with (tenant, vendor, N/A).  I will also have a Tenant and Vendor entity which both extend Organization and will differ by have collections of TenantUsers and VendorUsers respectively.
  2. Public ID.  TenantUser and VendorUser incrementing series per Tenant, and SuperUser will use the User PK.
  3. Permissions.  Done via interfaces and I don't think I will ever do based on the entity's properties/methods (but never say never).
  4. Based on business logic I will have some associations.  For instance, only a TenantUser can create a Project and will thus have a one-to-many projects property, but not the other user types (if created by SuperUser, they must select a TenantUser).  Then VendorUsers are assigned to the project, so there is a VendorUserAssignedToProject table/entity as well and only VendorUser will have a many-to-many assignedToProjects property.  I am becoming more and more leery about enforcing too much business logic in the database schema as I have too often done so and later needed to change it  For instance, maybe later a business rule is added which lets TenantUsers be assigned to projects.  But then again, can't worry too much about what ifs and I don't even know whether it should be done via a different table/entity. Round and round...

Regarding how they are similar, obviously the normal properties (username/password/etc), but also audit trails such as who modified a record or uploaded a document.

On 10/27/2021 at 6:01 AM, NotionCommotion said:

Others will say "Use inheritance when you want to add functionality and use composition when you want to modify functionality.

My current predicament doesn't completely fit but hoping it does enough.

The reason I said this is I am changing the User entity based on who it is associated by using inheritance which conflicts with this recommendation.  Ignoring the SuperUser for now, thinking I should just have a User which has an organization property which could either be a Tenant or Vendor, but not sure how I need to implement this if Doctrine is used.

Link to comment
Share on other sites

Unless I missed it, I didn't hear about any data that's different between the various users. Which means it's easier to go with a flattened approach: one single table holds all the users, and you can store the user type in a field.

In code you can have multiple user classes with different methods for the different functionalities (and without any additional data). All three inherit from the base class and otherwise aren't related to each other.

I don't see how traits or even interfaces fit into this.

Link to comment
Share on other sites

9 hours ago, requinix said:

Unless I missed it, I didn't hear about any data that's different between the various users. Which means it's easier to go with a flattened approach: one single table holds all the users, and you can store the user type in a field.

In code you can have multiple user classes with different methods for the different functionalities (and without any additional data). All three inherit from the base class and otherwise aren't related to each other.

I don't see how traits or even interfaces fit into this.

  1. All entities belong to a given tenant except for a few exceptions: 1) Tenant 2) SuperUser 3) common tables such as US state.
  2. All entities which belong to a tenant have a public identifier which is unique per Tenant.
  3. All VendorUsers belong to a given Vendor (which in turn belongs to a given Tenant).

Therefore, tenant_user and vendor_user both have a tenant_id column to ensure isolation and a public_id column for the identifier, but not super_user.

Furthermore, tenant_user and vendor_user both have a organization_id column which references tenant and vendor respectively for the many users have one organization relationship not super_user (user.organization_id is just to ensure unique email, etc and is not a FK and always 0 for a SuperUser).

As such, tenant_user, vendor_user, and super_user all have different data.

Before adding SuperUser, both TenantUser and VendorUser extended AbstractEntityBelongsToTenant which contained the publidId and tenantId properties as well as associated methods, but this doesn't work with SuperUser, and that is why I thought I needed to either use a trait or copy and paste the code.  Disagree?

PS. Tenant and Vendor extend from Organization but not at the DB level as there are no tables that reference either tenant.id or vendor.id, and while the SQL schema would look "prettier", I didn't see any other benefits.

PSS. tenant_user.tenant_id and tenant_user.organization_id are always identical and I am considering getting rid of tenant_user.organization_id and renaming vendor_user.organization_id as vendor_user.vendor_id.

 

 

image.thumb.png.09799b6dffdd10be0356748029e75499.png

Link to comment
Share on other sites

IMO, your whole model is off. You would really benefit from implementing the "Party Model". Without getting too deep into it,... at the root you have a Party. A Party is either a Person or an Organization. It then branches off from there. The Party Model is infinitely scalable without having to ever re-design anything.

* When you start duplicating things, i.e names, phone numbers, etc, that is a good sign you probably have a design problem.

Here is an example.

Capture.PNG

Edited by benanamen
Link to comment
Share on other sites

19 hours ago, benanamen said:

IMO, your whole model is off. You would really benefit from implementing the "Party Model". Without getting too deep into it,... at the root you have a Party. A Party is either a Person or an Organization. It then branches off from there. The Party Model is infinitely scalable without having to ever re-design anything.

Thanks benanamen,  Checking out the Party Model.  I found some information but nothing incredible.  Any recommendations.

19 hours ago, benanamen said:

* When you start duplicating things, i.e names, phone numbers, etc, that is a good sign you probably have a design problem.

I agree and am back and forth.  I think doing as I did breaks the 5th normalization rule and I probably should make organization its own table.

On 11/2/2021 at 6:09 AM, NotionCommotion said:

PS. Tenant and Vendor extend from Organization but not at the DB level as there are no tables that reference either tenant.id or vendor.id, and while the SQL schema would look "prettier", I didn't see any other benefits.

 

Link to comment
Share on other sites

3 hours ago, NotionCommotion said:

Any recommendations

The best books I have found that deals with the Party Model A.K.A "Universal Data Model" is "The Data Model Resource Book" volumes 1-3 by Len Silverston. Your project is a perfect fit for this data model.

You will end up needing to re-think how you are currently looking at your data. There are no tenents or vendors. There are parties that have one or more roles that could be a tenent, vendor or anything else. it is possible that a party has multiple roles such as being a tenent AND a vendor. Your current model could not handle that without duplicating data. The party model, no problem and no redesigning or duplicate data whatsoever.

https://www.amazon.com/Data-Model-Resource-Book-Enterprises-ebook/dp/B006BBVQQY

https://www.amazon.com/gp/product/B0033AHGMY/ref=dbs_a_def_rwt_bibl_vppi_i0

https://www.amazon.com/gp/product/B004U7MUOS/ref=dbs_a_def_rwt_bibl_vppi_i1

 

 

Link to comment
Share on other sites

8 hours ago, benanamen said:

The best books I have found that deals with the Party Model A.K.A "Universal Data Model" is "The Data Model Resource Book" volumes 1-3 by Len Silverston. Your project is a perfect fit for this data model.

You will end up needing to re-think how you are currently looking at your data. There are no tenents or vendors. There are parties that have one or more roles that could be a tenent, vendor or anything else. it is possible that a party has multiple roles such as being a tenent AND a vendor. Your current model could not handle that without duplicating data. The party model, no problem and no redesigning or duplicate data whatsoever.

Are you sure it wasn't written not by Len Silverston but by Karl Marx?  Just kidding, just kidding!

EDIT.  Sorry, hit submit before I thought.  Still kind of funny.  Will definitely look into and much appreciated.

Edited by NotionCommotion
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.