1

When I call an API endpoint I have a class to create an object with the response. The object directly maps the endpoint elements so I have all values, with properties, getters, and setters.

In my problematic scenario I want to set object(s) for 2 endpoints which both represent the same entity, however one has an additional element (logical for the way the API is structured) which is stopping me just creating one object.

It's the "Landlord" and "Manager" entities I want to manage (eg ignore a need for an object on properties endpoint):


ENDPOINT 1: /landlord/{id}/:

Landlord Response:

[
    'id' => 10,
    'first_name' => 'Bob',
    'surname' => 'Jones',
    'properties = [
        0 = [
            'id' => 20,
            'street' => 'whatever',
            'postcode' => 'jj1144ll',
        ],
    ]
]

ENDPOINT 2: /properties/{id}/:

Properties Response:

[
    'id' => 20,
    'street' => 'whatever',
    'postcode' => 'jj1144ll',
    'manager' = [ // This is a "Landlord"
        'id' => 10,
        'first_name' => 'Bob',
        'surname' => 'Jones',
    ]
]

The responses are simplified but "Landlord" has many other things, "DOB", "address", etc.

The Problem:

"Manager" on /properties{id}/ has all the same elements to represent a landlord, but /landlord/{id}/ has Properties array which /properties/{id}/ of course does not, as it is a property.

So I can't just create one object for both as while a manager is a landlord and should be one object to represent this, there is that array on landlord endpoint.

I'm entirely open to suggestions but here's what I have come up with so far:


OPTION 1 - One individual class for each endpoint.

Classes/objects:

  • Landlord
  • Manager

PRO: "landlord" can have the "properties" array on it's object, and "manager" simply doesn't have it.

Issues:

  1. Duplication - Both classes will have properties, getter, setter for all the elements (about 15) so duplicated in both classes.
  2. I'm defining "manager" as a separate entity when really a manager is a landlord. Having a separate "manager" class/object (and factory etc) suggests it's something different.

OPTION 2 - Just have one class.

Class/object:

  • Landlord

PRO: Responses for both "landlord" and "manager" just use the same object (and factory etc). So it's clear "manager" is a "landlord".

Issues:

  1. Could be confusing as it's unclear where "manager" object is without pre-knowledge of the system.
  2. I have no idea how to manage the additional array that "landlord" data has that "properties" doesn't. We're heading down the territory of if isset().

OPTION 3 - One shared class for matching elements and one individual for the other 2 endpoints.

Classes/objects:

  • LandlordOrManager
  • Landlord
  • Manager

PRO: Both "landlord" and "manager" data have their own class and so can set their own individual data. Those classes would both use the shared one to get shared data, getters/setters etc (somehow?).

Issues:

  1. This just feels cumbersome and I don't think the intent is clear, nor would devs know what object they want/need.
  2. I'm still referring to "Manager" as a separate thing, and have a separate class/object for it. It's actually a "landlord" and perhaps the code should reflect this?

I prefer Option 3 to avoid mass duplication given both "landlord" and "manager" objects are really the same, and I think it's more important to represent that, but I'm not sure how to:

  1. Name the shared class to show intent without it being horrid.
  2. Structure the code so other devs know what it's all about.
  3. Utilise the 3 classes correctly. Extend seems obvious but not sure if this is inappropriate/smell etc.

Hopefully you can see my issue and suggest something. I could be over thinking this but I just can't see a way around issues from either option.

4
  • This has taken a lot of my time reading and researching to try to figure out myself but to no avail. I know it's a fair read but will absolutely appreciate any advice etc. It'll help with numerous scenarios I have at the moment!
    – James
    Commented Nov 11, 2019 at 7:28
  • just can't see a way around issues from either option. And you won't until you just test one and then the next. It's unlikely more options will help you. I wonder if more options just will make things more confusing and make hard the choice. It's preferable to do a proof test, you will get some answers and then new questions, but those questions will be more concrete and easier for the community to answer. Bear in mind that there'is going to be "always" trade-offs. There are no silver bullets here.
    – Laiv
    Commented Nov 11, 2019 at 7:43
  • @Laiv I'm happy for someone to point me in the right direction, not just pick one of my options :) "I'm entirely open to suggestions but here's what I have come up with so far"
    – James
    Commented Nov 11, 2019 at 7:46
  • Interesting question. Note that it seems somewhat symmetric with properties, since the landlord endpoint offers properties, which are the same as in the property endpoint, except that the property endpoint attaches to them a manager that the other does’t have.
    – Christophe
    Commented Nov 11, 2019 at 8:42

2 Answers 2

1

One way to deal with this is to treat the manager and properties attributes as references. Just as an <a> tag in an HTML document has a href attribute and some content which describes the linked-to document within the context of the document containing the link, the contents of your manager and properties attributes should be "link" objects containing just enough attributes from the linked-to object that the link can be reasonably presented to the user.

Example (trying to write valid JSON):

/landlord/10:

{
    'id': 10,
    'first_name': 'Bob',
    'surname': 'Jones',
    'properties': [
        {
            'id': 20,
            'ref': '/properties/20',
            'street': 'whatever',
            'postcode': 'jj1144ll'
        }
    ]
}

/properties/20:

{
    'id': 20,
    'street': 'whatever',
    'postcode': 'jj1144ll',
    'manager': {
        'id': 10,
        'ref': '/landlord/10',
        'first_name': 'Bob',
        'surname': 'Jones'
    }
}

I understand that this is a public API that you cannot change. If you can't sneak in a 'ref' attribute you probably have to leave it out, the 'id' attribute would work for a knowledgeable client even though it is not really HATEOAS (note I'm not a true believer or proponent of HATEOAS, it's just a mental model that allows me to think clearer about RESTful design).

0

You have your model warped.

A landlord is not a manager with a hotel attached to its butt. Your entity is hotel, which has manager as a property.

Now all your problems are gone.

2
  • This is from a public API I cannot change that. A landlord on landlord is a landlord, a manager on properties is a landlord. They have the same IDs as they are essentially an IRI. When I want to update a landlord's data with a property endpoint response (manager) it is a landlord - maybe I shouldn't do that
    – James
    Commented Nov 11, 2019 at 8:00
  • OK, so you only have control over the way you map this flawed API to your own objects and there is no real difference between a manager and landlord. How about mapping landlord to manager, not recognize they are two things on your end, treat a landlord as a manager and give your manager an optional properties, or a fixed properties collection that may be empty? Commented Nov 11, 2019 at 8:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.