← Back to Upcase

Model design with nested data structures


(Dan Scotton) #1

Couldn’t think of a more descriptive title so let me just jump straight in. I run into this problem a lot, and I’m never quite sure about the best way to go about it.

Say I have the following API response:

{
    type: 'STY',
    title: 'My story title',
    summary: 'My story summary,
    site: {
        name: 'BBC News',
        code: 'news-v6'
    }
}

This represents a story on the BBC News website, which we transform into a Story model. The same type of story can exist on the BBC Sport website for example, but it would have a site property of:

{
    ...
    site: {
        name: 'BBC Sport',
        code: 'sport-v6'
    }
}

In order to grab the story’s site name, I could either create a method on Story (sorry, we use PHP):

class Story
{
    public function getSiteName()
    {
        return $this->site['name']; 
    }
}

If I want to grab more data out of site, I have to create more methods on Story: getSiteCode(), getSiteFoo(), etc. Alternatively, I could represent that piece of meta data in a Site object which would be accessible using a getSite() method on Story:

class Story
{
    public function getSite()
    {
        return $this->site; // where $site has already been parsed into a Site object
    }
}

class Site
{
    public function getName()
    {
        return $this->name;
    }
}

The problem I have with this approach is that it breaks the law of demeter. If I want to get the site name of a Story, I’d have to do something like:

$this->story->getSite()->getName();

Does anyone else run into issues like this, and do you have a preferred way of handling it?

Cheers!


(Dolph Mullen) #2

I use PHP a lot, and I’ve had the same thoughts as you, which is mostly getting discouraged by its syntax and OO implementation. Mainly this is an example of how PHP is a lot less elegant than ruby.

In this case, I think the better method might be to do a merge of your two ideas. In PHP it might look like this:

class Story
{
	...
	
	public function getSiteName()
	{
		return $this->site->getName();
	}

	public function getSiteCode()
	{
		return $this->site->getCode();
	}
}
class Site
{
	...
	
	public function getName()
	{
	        return $this->name;
	}

	public function getCode()
	{
		return $this->code;
	}
}

We are delegating to the Site in order to get it’s data, obeying Demeter. This is a common pattern and just looks a lot more friendly in a language like Ruby:

class Story
  ...
  
  def site_name
    @site.name
  end
  
  def site_code
    @site.code
  end
end
class Site
  attr_reader :name, :code
  ...
  
end