Skip to content

Instantly share code, notes, and snippets.

@bananatron
Last active June 23, 2020 22:48
Show Gist options
  • Save bananatron/165ba1ef1cbe586145920267f1565684 to your computer and use it in GitHub Desktop.
Save bananatron/165ba1ef1cbe586145920267f1565684 to your computer and use it in GitHub Desktop.
WorksPagesStructureProposal.md

Proposed Component Structure

This is an entry/discussion point for a suggestion which follows the first example on the react file structure page, but with more specificity, catered to what we are actually building. This ruleset prioritizes speed of development and context understanding over component conformity & isolation.

The general rules:

  • Our react front-end's job is to visually draw stuff on a web page. As a result, the file structure should match the structure of the visual behavior of our application.
  • Be explicit about which components can be shared outside of their domain/page and which can't (common folders exist on all domain levels).
  • Use your best judgement when you should/shouldn't extract things. We're all grown ups and we're all rowing in the same direction. We have designs and we'll have good guesses about what will be reused and what won't.
  • We have a convention on words which indicate a page's primary function - maybe this looks like index/list, show, edit, delete, etc. When pages fall under these categories, we use these words (90% of the pages we build will use these words).
  • Components (when possible w/out degradation to the UX) should fetch the data they need. This takes advantage of apollos cacheing mechanism, simplifies code, and speeds up development. Otherwise, pages fetch data they need and pass it to children.
  • Components folders always have one entry point.
  • All the good general rules about markup composition should apply - don't put padding/margin on children, components only style themselves, components should restrict mutations to their parents, etc. etc.

Pros

  • This structure does NOT have strong opinions on how components should behave based on where they live.
  • The general file structure represents how the app is visually represented.
  • Having domain-dependent common folders make their growth more manageable (vs. one giant ui/features folder) and give you explicit context into where it was designed to be consumed (if at all).
  • This is a small iteration of what we already have, it doesn't require a rewrite which is a huge paradigm shift from what's in works, currently.
  • One of the advantages of graphql is that you can define queries that only you need, but we are losing some of that by assuming a query for a resource will always be the same on the global level - this opens up a pattern for domain specific gql defenitions (supplier/pm/global) as well as componenet-specific definitions.

Cons

  • This structure does NOT have strong opinions on how components should behave based on where they live.
  • Having 100 index.js files seems like kind of a pain for code navigation and fuzzy searching.
  • This relies on developers and code reviews to take responsibility for isolating components (where practical) and getting them into storybook, when applicable (in a future world).
  • This does not match the marketplace/manage app structure.

Sample Structure

.
├── graphql
├── auth-failure
│   │   ├── index.js
├── auth-guard
│   │   ├── index.js
├── common
│   ├──loading-state
│   |   └── index.js
│   ├──empty-content
│   |   └── index.js
│   ├── user-avatar
│   │   └── index.js
├── supplier
|   ├── graphql
|   ├── utils
│   ├── common
│   │   ├──candidate-profile
│   │   |   └── index.js
│   │   |   └── candidate-profile-specific-widget.js
│   │   ├── candidate-form
│   │   |   └── index.js
│   │   |   └── license-input.js
│   │   |   └── submittal-packet-input.js
│   │   ├── edit-license-modal
│   │   |   └── index.js
│   |   └── upload-document-modal
│   |       └── index.js
│   ├── pages/components
│   │   ├── dashboard
│   │   |   └── index.js
│   │   ├── candidates-list
│   │   |   └── index.js
│   │   |   └── candidate-list-specific-widget.js
│   │   ├── candidate-show
│   │   |   └── index.js
│   │   ├── candidate-new
│   │   |   ├── index.js
│   │   ├── jobs-list
│   │   |   └── index.js
│   │   └── job-show
│   │       └── index.js
└── pm
    ├── graphql
    ├── utils
    ├── common
    |   └── staffing-request-header-bar
    │       └── index.js
    └── pages/components
        ├── dashboard
        |   └── index.js
        ├── reports
        |   └── index.js
        ├── staffing-request-list
        |   └── index.js
        ├── staffing-request-edit
        |   ├── graphql (e.g has unique queries that probably won't be shared)
        |   |   └── update-staffing-request.gql
        |   └── index.js
        └── staffing-request-show
            └── index.js

Issues this doesn't resolve:

  • Where do we put shared front-end business-logic that is not visual? If I have 3 components on three different pages that all need to determine if a WorksListItem is approvable based on a derived front-end formula, where does that live? Perhaps we have 'components' that are in common that are not visual at all? Maybe we extract the area where it WILL be visual to a component which all three places use? Potentially we have a folder on the root level that maps to business objects/rails models? With this structure, utils (which could be called helpers/lib/etc.) is a good candidate in the domain context, but it's the wild west 🤠

FAQ

I'm building a new feature, where do I put the code?

Does the feature live on one page? Add it to that page component's folder. Does it need data access? Fine, fetch data on the component or extend the fetching to another component if it's easier to create a stateless component for storybook. Is the feature shared among components? Put it at the highest level common folder where it will be shared (global, pm, supplier, etc.). If it's complex, break it into small, testable components that live inside one folder, or leave it as one complex component if it's easier to read/update in the future.

I need to use a component on another page and it's not in a common folder, what do I do?

It's your job to move that component to common and make it work with all consumers. Breaking your pages into many common components is a good idea and will make it easier for other people to use this (but because we have designs for the apps we're building, we probably will know when things will be consumed in many places).

What happens if we change the navigation of the app or name of the label on the sidebar, etc.?

We change the names of folders, and maybe the names of components. This rarely happens and is really easy to do.

TLDR; We know what we're building, this is not an abstract problem. We are building two user-facing CRUD apps inside works. Those two apps have pages which have forms and tables and modals. This has been done before. We shouldn't need to make complex decisions about building forms.

We already have enough to worry about onboarding folks w/ react, gql, rails, heroku, [insert tool you didn't use before trusted], having to do constant global searches and navigating boilerplate while building a new features seems like a bummer, in addition to the contextual costs of doing a migration to an entirely new structure (iterative or not).

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