Skip to content

Instantly share code, notes, and snippets.

@reinink
Created April 11, 2016 10:11
Show Gist options
  • Save reinink/504a9654c4348dbc0dc81ff4313fb588 to your computer and use it in GitHub Desktop.
Save reinink/504a9654c4348dbc0dc81ff4313fb588 to your computer and use it in GitHub Desktop.
How do you typically organize your classes?

By type

  • /Controllers
  • /Events
  • /Models

By topic

  • /Comments
  • /Posts
  • /Users

By topic, then type

  • /Comments
    • /Controllers
    • /Events
    • /Models
  • /Posts
    • /Controllers
    • /Events
    • /Models
  • /Users
    • /Controllers
    • /Events
    • /Models

Mixed

  • /Comments
  • /Controllers
  • /Events
  • /Models
  • /Posts
  • /Users
@reinink
Copy link
Author

reinink commented Apr 11, 2016

@Jarlskov My concern with that approach is when you have business domain language that conflicts with infrastructure language. For example, maybe your app has Policies, but your business domain also has the concept of policies. Then it gets messy.

@frankdejonge
Copy link

At the top level I separate early into two categories: infrastructure and domain. This is where the "not being coupled to the framework" is guarded. In the domain I only separate by context, not topic, not type. A simple "test" for a namespace would be to see if you can use it in a sentence like "How are/is Y defined in the context of X". This takes into account that an idea/entity/vo/concept is represented per context in which it's applicable. For instance, when you have hour tracking in the context of a user "Hours Worked" may have a different representation when in the context of "reporting" or "management".

@reinink
Copy link
Author

reinink commented Apr 11, 2016

Expanding on the "By topic, then type" idea, here is what we've done on a larger recent project, and it worked out pretty well.

  • /Comments
    • /Controllers
      • /CommentsController.php
    • /Events
      • /NewCommentEvent.php
    • Comment.php
  • /Posts
    • /Controllers
      • /PostsController.php
    • /Events
      • /NewPostEvent.php
    • /Post.php
  • /Users
    • /Controllers
      • /UsersController.php
    • /Events
      • /UserLoggedInEvent.php
      • /UserSubscribedEvent.php
    • /User.php

@reinink
Copy link
Author

reinink commented Apr 11, 2016

@frankdejonge That really interesting. Could you should share a more concrete file structure example, similar to the other examples?

@elphia
Copy link

elphia commented Apr 11, 2016

That make sense in a big project, but only for the Topics that worth it in my opinion.

If you have only One Controller and One Model for a Topic, I wont git it it's own folder.

But if you have multiple controllers in your Post for example, It make sense to give Post its own folder for childs controllers and models.

@frankdejonge
Copy link

@reinink: sure thing.

  • /src
    • /Users
      • /User.php
      • /UserLoggedIn.php
      • /UserSubscribed.php
    • /Posts
      • /Post.php
      • /PostWasCreated.php
      • /PostWasDeleted.php
  • /infra
    • /Controllers
      • /UserController.php
      • /PostController.php

This would be the basic setup. Controllers are framework things, views would optionally reside here too. To me events are explicitly part of the domain. The domain emits and responds to them. This is one of the reasons I created league/events so you can have a domain focused lightweight events system. This also creates a nice separation between domain events and infra events, which both have valid reasons to exist. The same goes for commands, in symfony and laravel a "command" might be a console command, which is perfectly fine. However, in the domain it probably makes more sense to have commands as well but then let them be commandbus commands. Again, both are valid. I also try to steer clear from type suffixing in the domain. UserHasLoggedIn is a description of an event, thus event is implied. While the Event suffix might be a common thing to see it's all about "naming patterns". So, let's apply the type suffing to something else in our domain. If we have a User and a user needs to be verified before the user can log in we may keep track of that with a boolean. If we were to create a class to represent this boolean, would we name it ActivationBoolean? Probably not. Anywho, I like to think about those kind of things as well, all in the context of scoping/grouping/naming things.

@shochdoerfer
Copy link

@frankdejonge comes close to what we use these days, except for a slightly different naming convention. We call "infra" "Web" or "Http" to give a better understanding what that package deals with. And in case we need a CLI package, those classes will get placed in a "Cli" folder. In addition to that we place all classes in the src folder. I do not like the idea of having multiple src folders even though I get your intention ;)

@philipobenito
Copy link

It depends on the project for me, if it has a large enough scope then I would usually do something quite similar to @frankdejonge but if I was building a small microservice for example I would tend to go by type.

  • /src/
    • /Controller/
      • UserController.php
    • /Repository/
      • UserRepository.php
    • /Store/
      • RedisStore.php
      • SqlStore.php

@reinink
Copy link
Author

reinink commented Apr 11, 2016

@frankdejonge Awesome, thanks for sharing. Your goal to keep framework dependencies separated from your domain code is interesting. On one hand this sounds really smart, while on the other hand I wonder how well this works out, practically. I wonder if this causes you to not use a bunch of helpful, built-in framework tooling, simply because you're trying to be framework agnostic. Consider if you want to use a Collection class, which the framework supplies. Do you use a framework agnostic collection library instead? Seems odd?

As for dropping the suffixes, I go back and forth. I don't suffix language types (interfaces, booleans, etc.), but I will use class types (events, controller, validator, etc). Otherwise I find it too difficult to find things, since often different classes can share the same name, even though they are different types.

@seeruk
Copy link

seeruk commented Apr 11, 2016

"By topic, then type" would also be known as a modular structur. It's generally a good idea simply because if you design your solutions to be modular you'll be able to expand them easily, while keeping code organised. See things like Symfony's bundles for example.

@aminemat
Copy link

By topic (sub-domain), entities in the sub-domain root folder, then by type
example with 2 topics, Shifts and Employees:

structure

@frankdejonge
Copy link

@shochdoerfer I'm still toying with the 1 dir 2 dirs X dirs setup. One thing I don't like is to have to group the domain in an arbitrary namespace. I often see the Web/Http namespacing, but then it comes with a "Domain" namespace too which pretty much always puts me off.

@stefanbauer
Copy link

Even this is a really, really old gist, I'd still just wanted to share my thoughts on it, which I wrote a blog post about it: https://stefanbauer.me/articles/an-alternative-way-to-organize-the-laravel-directory-structure - I think it's basically what @frankdejonge is doing. Do it for several years now and still happy with it – at least if we're talking about larger projects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment