Advertisement
Guest User

Untitled

a guest
Jan 23rd, 2017
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.36 KB | None | 0 0
  1. I’ve been working on very large web applications for the past few years, starting from ground zero and, with a dozen other developers, making them scale up to now be used by millions of people. And sometimes, if you didn’t start with a good architecture, it can become difficult to keep your code organized.
  2. Nathanael Beisiegel wrote an interesting article where he explains his strategy in the organization of large React applications, but I still wasn’t completely satisfied by his approach. So I decided to spend some time to figure out what would be the best way to organize my future React projects.
  3. Note: I use Redux files in all the examples of this article. If you don’t know what Redux is, you can see the documentation here.
  4. Note2: The examples use ReactJS, but you can use exactly the same structure for a React-Native application.
  5. What are the challenges when you build an application?
  6. This has happened or will happen to pretty much all developers over the course of their career:
  7. You build an application for a client with a team of a few developers, everything works very well all together.
  8. Your client requires new features, fine, you add them.
  9. Your client asks you to remove some features and add new ones, it starts to get complicated, you didn’t think about that, but you make it work even though it’s not perfect.
  10. Your client now wants you to change another feature, remove some others and add another one that wasn’t expected. At this point, you grab the scotch tape and start patching some code. You are not very proud of this.
  11. 6 months later, after some other iterations, the code of the application gets really complicated to read and understand, everything looks like some Italian spaghetti pasta.
  12. Until the day your client decides to create a new version of the application, with some fresh new code and features. In some cases, you end-up keeping complicated legacy code that lives with the new code, and this becomes even harder to maintain. And all of this happened because your app wasn’t properly designed from the beginning.
  13. When I started to learn React, I found a few very good articles explaining how to create Todo lists or very simple games. Those articles were very useful to understand the basics of React, but I quickly got to a point where I wasn’t able to find much about how I could use React to build actual applications, something with a few dozens pages and hundreds of components.
  14. After some research, I learned that every React boilerplate project on Github results to similar structures, they organize all the files by type. This might look familiar to you:
  15. /src
  16. /actions
  17. /notifications.js
  18.  
  19. /components
  20. /Header
  21. /Footer
  22. /Notifications
  23. /index.js
  24. /containers
  25. /Home
  26. /Login
  27. /Notifications
  28. /index.js
  29. /images
  30. /logo.png
  31. /reducers
  32. /login.js
  33. /notifications.js
  34. /styles
  35. /app.scss
  36. /header.scss
  37. /home.scss
  38. /footer.scss
  39. /notifications.scss
  40. /utils
  41. index.js
  42. This architecture might be okay to build your website or application, but I believe that it is not the best architecture.
  43. When you organize your files by type, as your application grows, it often becomes difficult to maintain. By the time you realize this, it’s too late and you will have to invest a lot of time and money to change everything, or to support what you have for the next few years.
  44. The good thing with React is that you can structure your application in any way you like. You are not forced to follow a certain folder structure, React is simply a javascript library.
  45. What could be a better approach to organize your application?
  46. For a couple of years I worked for a financial institution which used Ember as their main javascript framework to build all their new web applications. One interesting thing about Ember is the ability to structure your project by features, instead of by type. And this changes everything.
  47. Pods in Ember are great but still limited, and I wanted something much more flexible. After a few experiments, trying to find what would be the best structure, I got to a point where I decided to group all related features together, and nest them as needed. This is what I use now:
  48. /src
  49. /components
  50. /Button
  51. /Notifications
  52. /components
  53. /ButtonDismiss
  54. /images
  55. /locales
  56. /specs
  57. /index.js
  58. /styles.scss
  59. /index.js
  60. /styles.scss
  61. /data
  62. /users
  63. /actions.js
  64. /api.js
  65. /reducer.js
  66. /scenes
  67. /Home
  68. /components
  69. /ButtonLike
  70. /services
  71. /processData
  72. /index.js
  73. /styles.jsx
  74. /Sign
  75. /components
  76. /FormField
  77. /scenes
  78. /Login
  79. /Register
  80. /locales
  81. /specs
  82. /index.js
  83. /styles.scss
  84. /services
  85. /api
  86. /geolocation
  87. /session
  88. /actions.js
  89. /index.js
  90. /reducer.js
  91. index.js
  92. store.js
  93. Each component, scene or service (an entity) has everything it needs to work on its own, such as its own styles, images, translations, set of actions as well as unit or integration tests. You can see an entity like an independent piece of code you will use in your app (a bit like node modules).
  94. To work properly, they should follow these rules:
  95. A component can define nested components or services. It cannot use or define scenes.
  96. A scene can define nested components, scenes or services.
  97. A service can define nested services. It cannot use or define components or scenes.
  98. A data entity is standalone.
  99. Nested entities can only use other entities that are defined by a parent entity.
  100. Note: By parent entity, I mean a parent, grandparent, great-grandparent etc… You cannot use an entity that is a “cousin”, this is not allowed. You will need to move it to a parent to use it.
  101. Let’s break this down.
  102. Components
  103. You all already know what a component is, but one important thing in this organization is the ability to nest a component into another component.
  104. Components at the root level of the components folder are global and can be used anywhere in your application. But if you decide to define a new component inside another component (nesting), this new component can only be used by an entity that is a parent.
  105. Why would you want to do that?
  106. When you develop a large application, it happens quite often that you need to create a component that you definitively know you won’t reuse anywhere else, but you need it. If you add it at the root level of your components folder, it will get lost with hundreds of components. Sure, you could categorize them, but when it’s time to do some clean-up, you won’t remember what they are all for or if they are still being used somewhere.
  107. Although, if you define at the root level only the main components of your application, such as buttons, form fields, thumbnails, but also more complicated one like listComments, formComposer with their own children components, it gets much easier to find what you need.
  108. Example:
  109. /src
  110. /components
  111. /Button
  112. /index.js
  113. /Notifications
  114. /components
  115. /ButtonDismiss
  116. /index.js
  117. /actions.js
  118. /index.js
  119. /reducer.js
  120. Button can be used anywhere in your application.
  121. Notifications can also be used anywhere. This component defines a component ButtonDismiss. You cannot use ButtonDismiss anywhere else than in the Notifications component.
  122. ButtonDismiss uses Button internally, this is authorized because Button is defined at the root level of components.
  123. Scenes
  124. A scene is a page of your application. You can see a scene just like any component, but I like to separate them into their own folder.
  125. If you use React-Router or React Native Router, you can import all your scenes in your main index.js file and setup your routes.
  126. With the same principle components can be nested, you can also nest a scene into a scene, and also define components or services into a scene. You have to remember that if you decide to define something into a scene, you can only use it within the scene folder itself.
  127. Example:
  128. /src
  129. /scenes
  130. /Home
  131. /components
  132. /ButtonShare
  133. /index.js
  134. /index.js
  135. /Sign
  136. /components
  137. /ButtonHelp
  138. /index.js
  139. /scenes
  140. /Login
  141. /components
  142. /Form
  143. /index.js
  144. /ButtonFacebookLogin
  145. /index.js
  146. /index.js
  147.  
  148. /Register
  149. /index.js
  150. /index.js
  151. Home has a component ButtonShare, it can only be used by the Home scene.
  152. Sign has a component ButtonHelp. This component can be used by Login or Register scenes, or by any components defined in those scenes.
  153. Form component uses ButtonHelp internally, this is authorized because ButtonHelp is defined by a parent.
  154. The Register scene cannot use any of the components defined in Login, but it can use the ButtonHelp.
  155. Services
  156. Not everything can be a component, and you will need to create independent modules that can be used by your components or scenes.
  157. You can see a service like a self-contained module, and could eventually be reused in any of your applications. What it does is really up to you, from a simple set of utility methods to a very complex subset of your application.
  158. /src
  159. /services
  160. /api
  161. /services
  162. /handleError
  163. /index.js
  164. /index.js
  165. /geolocation
  166. /session
  167. /actions.js
  168. /index.js
  169. /reducer.js
  170. Edit (Nov 2016):
  171. I have recently taken the decision to add an additional folder to this structure, called data. This change is more for organizational reason than functionality.
  172. Data
  173. A data entity is very similar to a service. You can see it as your bridge/an adapter between the server API and the client. You might not need this if your website or mobile app doesn’t process content from an API.
  174. It is in charge of most of the network calls your app will make, get and post content, and transform payloads as needed before being sent, or stored in the store of your app (such as Redux). In most projects, the data coming from an API is used by more than one scene or component, and needs to be available from anywhere.
  175. Wrapping up
  176. I’ve been working with this architecture for the past few months on a personal project built with React-Native, and I can tell you this saved me a lot of time. It’s much more simpler to have all related entities grouped together, it makes things easier to work with.
  177. This architecture is one of many other ways to organize your project, that’s the way I like it now and I hope this will help you improve yours!
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement