When it comes to building a web application, nothing causes more confusion than working out how to structure it (besides what to name things, of course).
In the beginning, it is easy enough to just throw all of your code into the root of the src
directory without a care in the world. But as the complexity of your application grows and subsequently its size, this approach fails.
I have been working on a large-scale Aurelia application for about 6 months now and have a structure that has allowed me to move quickly without worrying about clutter or where to put everything.
In the root directory we still have our; app.html, app.js (and if you one) the main.js file as well.
First up we have an assets
directory which contains sub-directories styles
and images
— you might also have a directory which contains fonts as well. This is where all styling and presentational stuff is kept (which is then handled by Gulp tasks).
Then we have a components
directory for storing common components that are applicable to the whole application on a global level. Such as a header include or navigation bar.
Followed by a pages
folder where all of our application specific logic lives. I create sub-folders inside of this folder to mirror my routes. For example if I have a route that is: http://myapp.com/users
then I would create a folder within pages
called users
For the index page of a particular route I will create a index.html
and index.js
View/ViewModel pair for this particular route. If users has sub routes, I will create more sub-folders to reflect the route. So if I have http://myapp.com/users/account/123
— then I would have a folder structure that looks like this:
pages
users
index.html
index.js
account
index.html
index.js
Matching your application directory structure and naming conventions to that of your router makes finding particular routes a whole lot easier than guessing which part of the application is rendering the current page you are viewing.
We also have a resources
directory which contains all of your value converters and global resources. Inside of my resources
folder I have two subfolders: value-converters
and custom-elements
which you can probably guess what those contain.
And then a lesser-important lib
directory which contains non-page/route specific code. I put in things like classes with custom functions for doing basic utility-like things in the application and adaptors for third-party libraries.
Another optional directory you might have is the services
directory which contains singleton based services. In here I have my service singleton classes for keeping track of the current user state, working with an API and so on.
Final Structure
If you skipped ahead or just found it hard to follow along, lets recap and structure our Aurelia application.
- src = Root directory and contains; app.html, app.js and main.js
- assets = Where all assets of the app live (with separate sub-folders)
- components = Where common application components like headers, navigation, footer, etc live
- lib = Where generic non-app specific code lives. Utility classes and functions for doing utility-like tasks
- pages = Where our route matching page Views and ViewModels live. This matches the predefined routes
- resources = Where our custom elements and value converters live
- services = A folder for singleton service classes like keeping track of state or working with API’s
src/
app.html
app.js
main.js
assets/
fonts/
images/
styles/
components/
lib/
pages/
page1/
index.html
index.js
page1Sub/
index.html
index.js
resources/
custom-elements/
value-converters/
services/
I like what you have here! I’m very new to Aurelia, so I may be completely off-base here. But I have a question that is more rhetorical, me thinking out loud, and looking to spur some discussion in the hopes that I can learn.
“Matching your application directory structure and naming conventions to that of your router makes finding particular routes a whole lot easier than guessing which part of the application is rendering the current page you are viewing.”
I can see how there are advantages to that. So I wonder why this isn’t one of Aurelia’s conventions in the spirit of “convention over configuration”? Aurelia could create default routes for everything under a specific directory (say, routes/) and then allow configuration to override that for special cases.
Question for you: I’m not fully grasping how you are differentiating between custom-elements and components. Would you be so kind as to elaborate on that and possibly share an example or two of each?
David,
The components folder is primarily for global view models that are not necessarily pages. For example you might have a base class that defines what a widget in a report builder application looks like. A component is something that other parts of your application either extend or include, but aren’t exactly tied down to anything. Not to be confused with services which deal with state (user, shopping cart, etc).
Thanks for this! Good to have a proper structure from day one as I’m just about to write our new admin panel in Aurelia.
When you say “Pages” do you mean like… Users? or Details? or something like that?