diff --git a/articles/building-apps/architecture/design.adoc b/articles/building-apps/architecture/design.adoc index 4a32c18f27..0498c46159 100644 --- a/articles/building-apps/architecture/design.adoc +++ b/articles/building-apps/architecture/design.adoc @@ -8,17 +8,19 @@ order: 10 // TODO Can you make the images zoomable by clicking on them? // No, but you can add links to them so the user can click and open them on a new page. +// Notes from Anna: +// would be nice if the Employee and Customer were in the same order in the first image than in the two following images. The fourth image is a bit more tricky to fit to the same model, though, unless you swap vertically the second and third images' contents, rather than just the first image's contents (which would be the easy solution). = Designing the Architecture -Software architecture is a wide concept. No one thing is the software architecture. It has many aspects and can be looked at from various points of view. This page presents only one of them: the structure of the software. +Software architecture is a wide concept: No one thing is the software architecture. It has many aspects and can be looked at from various points of view. This page presents only one of them: the structure of the software. // For other aspects, look at the <<{articles}/building-apps/architecture/deep-dive#,Deep Dive>> section of the documentation. -To explain a software architecture design to other developers, you need a good way of visualizing it. You've probably seen an architecture diagram. These aren't always helpful. Often, they are an incoherent and inconsistent mess of boxes and arrows that confuse rather than clarify. At the other end of the spectrum, you'll find dedicated modeling tools that use industry-standard notations such as Unified Modeling Language (UML) or Systems Modeling Language (SysML). These are without doubt useful but require you to learn a new notation and buy a dedicated modeling tool. +To explain a software architecture design to other developers, you need a good way of visualizing it. You've probably seen an architecture diagram. These aren't always helpful. Often, they're an incoherent and inconsistent collection of boxes and arrows that tend to confuse rather than clarify. At the other end of the spectrum, you'll find dedicated modeling tools that use industry-standard notations such as Unified Modeling Language (UML) or Systems Modeling Language (SysML). These are without doubt useful, but they require you to learn a new notation and use a dedicated modeling tool. Fortunately, there is a middle way called the _C4 model_. C4 is a model for visualizing software architecture in a notation and tooling-independent manner. The model is presented in detail at https://c4model.com[c4model.com], but the main ideas are summarized on this page. -The C4 model takes its inspiration from digital maps, where you can zoom in and zoom out. Higher zoom levels offer a better overview but have fewer details, whereas lower levels have more details while providing less overview. C4 allows you to create "maps" of your code and provides four zoom levels for you to use: Context, Containers, Components, and Code. You're not required to use all levels. It's fine to omit a level if you don't find it useful. There's no point in drawing diagrams for their own sake. +The C4 model takes its inspiration from digital maps, where you can zoom in and zoom out. Higher zoom levels offer a better overview, but have fewer details. Whereas, lower levels have more details while providing less overview. C4 allows you to create "maps" of your code and provides four zoom levels for you to use: Context, Containers, Components, and Code. You don't have to use all levels. It's fine to omit a level if you don't find it useful. There's no point in drawing diagrams for their own sake. == Context @@ -30,7 +32,7 @@ Here is an example system context diagram of a fictional appointment scheduling [[c4-context]] [.fill.white] [link=images/c4-context.png] -image::images/c4-context.png[A system context diagram of a fictional appointment scheduling system] +image::images/c4-context.png[A system context diagram of a fictional appointment scheduling system.] You can see that the system has two different types of users: employees and customers. You can also see that the system integrates with two external systems: a Customer Resource Management (CRM) system for storing customer information, and an online mailing service for sending emails to customers. @@ -38,6 +40,7 @@ The notation is simple: it consists of boxes, circles, lines, and text. It's imp You can add as much or as little text as you want, as long as it tells the story you want to tell. In the previous diagram you could, for instance, make the users larger and include more detailed descriptions of why each user might want to use the system. + == Containers If you were to zoom in on the software system in the context diagram, you'd get to the second level: a _container diagram_. In the C4 context, a container is either a deployable _application_ or a _data store_. It has nothing to do with Docker containers -- even though you may later package your applications as such. A container diagram shows the deployable parts of the system and how they interact with each other and any external systems. @@ -46,24 +49,24 @@ Continuing with the fictional appointment scheduling system example, zooming in [.fill.white] [link=images/c4-container.png] -image::images/c4-container.png[A container diagram of a fictional appointment scheduling system] +image::images/c4-container.png[A container diagram of a fictional appointment scheduling system.] -As you can see, the diagram reveals a lot more details than the context diagram. You can see that the system consists of six containers: two web browsers (the employee's and the customer's), two Vaadin applications, a Hazelcast shared cache and a PostgreSQL database. You can see that the employee-facing user interface is using Flow and the customer-facing user interface is using Hilla and React. You can also see how the containers communicate with each other and with the external systems. +As you can see, the diagram reveals plenty more details than the context diagram. You can see that the system consists of six containers: two web browsers (i.e., the employee's and the customer's); two Vaadin applications; a Hazelcast shared cache; and a PostgreSQL database. The employee-facing user interface is using Flow and the customer-facing user interface is using Hilla and React. The containers communicate with each other and with the external systems. The notation follows the same pattern as the system context diagram: boxes, circles, lines, and text. You can add as many details as needed. For instance, if modeling the web browsers as separate containers isn't relevant, you could simplify the diagram like this: [.fill.white] [link=images/c4-container-simplified.png] -image::images/c4-container-simplified.png[A simplified container diagram of a fictional appointment scheduling system] +image::images/c4-container-simplified.png[A simplified container diagram of a fictional appointment scheduling system.] A Vaadin developer would be able to deduce from this diagram that the users are using their web browsers to interact with the applications. == Components -If you were to zoom in on a container in the container diagram, you would get the third level: a _component diagram_. The term _component_ is used quite liberally in the software industry, often for different things depending on the context. In the C4 context, the term component is defined as an encapsulation of related functionality that has a well-defined interface and that can be instantiated. +If you were to zoom in on a container in the container diagram, you would get the third level: a _component diagram_. The term _component_ is used quite liberally in the software industry, with its meaning dependent on the context. In the C4 context, the term component is defined as an encapsulation of related functionality that has a well-defined interface and that can be instantiated. -However, as Vaadin also uses components to construct user interfaces, it makes sense to further the scope and type of a component with a prefix. Therefore, the components that you find in a component diagram are _system components_ and the components you find in a Vaadin user interface are _UI components_. +As Vaadin also uses components to construct user interfaces, it makes sense to further the scope and type of a component with a prefix. Therefore, the components that you find in a component diagram are _system components_ and the components you find in a Vaadin user interface are _UI components_. A component diagram shows how a container like a Vaadin application is constructed from system components. It includes information about the components' responsibilities, how they are implemented, and how they interact. @@ -71,24 +74,25 @@ Continuing with the fictional appointment scheduling system example, zooming in [.fill.white] [link=images/c4-component.png] -image::images/c4-component.png[A component diagram of a scheduling application] +image::images/c4-component.png[A component diagram of a scheduling application.] As you can see, the notation is again the same as in the past two levels. If you had access to the source code of the scheduling application, this diagram would already be quite helpful in navigating it. You can see that there are six system components: -- The _Views_ system component contains the Flow user interface. -- The _Booking App Endpoints_ system component contains the https://grpc.io[gRPC] endpoints that the Booking App uses. -- The _Application Services_ system component contains the business logic of the entire application. It has an API that is called by both _Views_ and _Booking App Endpoints_. -- The _Entities_ system component contains the entities and repositories and uses Spring Data and JPA. It has an API that's called by _Application Services_. -- The _CRM Integration_ and _Online Mailing Service Integration_ system components act as adapters between the scheduling application and the external CRM system and online mailing service, respectively. They both implement Service Provider Interfaces (SPI) that _Application Services_ has defined. +- _Views_ system component, which contains the Flow user interface. +- _Booking App Endpoints_ system component: it contains the https://grpc.io[gRPC] endpoints that the Booking App uses. +- _Application Services_ system component contains the business logic of the entire application. It has an API that's called by both _Views_ and _Booking App Endpoints_. +- _Entities_ system component contains the entities and repositories and uses Spring Data and JPA. It has an API that's called by _Application Services_. +- _CRM Integration_ and _Online Mailing Service Integration_ system components act as adapters between the scheduling application and the external CRM system and online mailing service, respectively. They both implement Service Provider Interfaces (SPI) that _Application Services_ has defined. Getting the system components right is essential to succeeding in building great Vaadin applications. You can learn more about APIs and SPIs of system components and how to turn system components into Java code on the <> page. + == Code -If you were to zoom in on a system component in the component diagram, you would get to the fourth and final level: a _code diagram_. This shows how a system component has been implemented in code and is typically drawn using UML or some other standard notation for code design. It's the most detailed of all the diagrams and, therefore, likely the one you'll use the least. +If you were to zoom in on a system component in the component diagram, you would get to the fourth and final level: a _code diagram_. This shows how a system component has been implemented in code and is typically drawn using UML or some other standard notation for code design. It's the most detailed of all of the diagrams and, therefore, likely the one you'll use the least. -The code is the ultimate source of truth and it changes often, especially at the beginning of a project. This means that any code diagrams you draw are likely to become outdated at some point. It's therefore recommended to make code diagrams only for the most complex components. You can keep them up to date or discard them when they are no longer useful. The main point is to only draw the diagrams that help you tell the story and get the job done. +The code is the ultimate source of truth and it changes often, especially at the beginning of a project. This means that any code diagrams you draw are likely to become outdated at some point. It's therefore recommended to make code diagrams only for the most complex components. You can keep them up to date or discard them when they are no longer useful. The main point is to draw only the diagrams that help you tell the story and get the job done. // TODO Add links to articles once they have been written diff --git a/articles/building-apps/architecture/index.adoc b/articles/building-apps/architecture/index.adoc index d8ee46bc37..31a7781b73 100644 --- a/articles/building-apps/architecture/index.adoc +++ b/articles/building-apps/architecture/index.adoc @@ -4,3 +4,4 @@ description: Learn how to architect business applications with Vaadin. order: 10 --- +// Notes from Anna: feels a little bit disjointed since the descriptions don't follow the same format with each other. Maybe at least switch the 'Learn' into 'Teaches' or something :thinking_face: Or just drop the verbs from the front of all items. \ No newline at end of file diff --git a/articles/building-apps/presentation-layer/index.adoc b/articles/building-apps/presentation-layer/index.adoc new file mode 100644 index 0000000000..c1b56357e3 --- /dev/null +++ b/articles/building-apps/presentation-layer/index.adoc @@ -0,0 +1,5 @@ +--- +title: Presentation Layer +description: Learn how to build the presentation layer of your Vaadin app. +order: 30 +--- diff --git a/articles/building-apps/presentation-layer/view-structure/composition.adoc b/articles/building-apps/presentation-layer/view-structure/composition.adoc new file mode 100644 index 0000000000..0442fe4043 --- /dev/null +++ b/articles/building-apps/presentation-layer/view-structure/composition.adoc @@ -0,0 +1,117 @@ +--- +title: View Composition +description: TODO write description +order: 10 +--- + +// Notes from Gerald: +// +// General +// - thereis a lot of "You should find this out before you start implementing." Its a bit of an overhead. maybe this should be put into a little colored box at the side with a microscope or something to indicate the things the developer has to find out to not interrupt the reading flow. +// +// Main layout +// - the word layout is a bit confusing. you are talking about the router layout which shouldn't be revealed yet, but i was confused in the beginning with horizontal/vertical/flex/etc. layout. as you came to "a view is rendere inside a layout" it became clear to me which is which. so maybe we need a more abstract term here? +// +// The View +// - what do you want to say with that: "The sidebar and header are so simple that they can be nested inside the panel."? + + += View Composition + +Vaadin user interfaces are built by composing UI components together. Some UI components are provided out-of-the-box by Vaadin, while others you have to write. Some components are generic and reusable in different settings; others are designed for one specific use case and should not be reused. Knowing how to identify and implement these components is key to succeeding in building complex Vaadin user interfaces. + +This page explains the basics of view composition in Vaadin. The starting point is the following mock-up of a fictional business application: + +image:images/application.png[alt=A mock-up of a fictional business application,width=800] + +What follows are the steps as to how to divide the application into smaller parts until you're ready to implement it. + + +== Main Layout + +The first step is to identify which parts of the user interface belong to the _layout_ and which belong to the _view_. A layout contains elements that are always visible in the user interface, such as the title of the application, navigation links and information about the current user. A view is one page of the user interface and is typically rendered inside a layout. As the user navigates between views, the layout remains the same. It's also possible to nest layouts inside another layout. + +The main layout of the mock-up is shown in the image here: + +image:images/main-layout.png[width=800] + +This consists of a header and a content area. The content area contains whatever is the current view. The header consists of three different components: the application's logo; navigation links to the _Employees_, _Teams_ and _Locations_ views; and the current user's avatar. + +The main layout is a UI component you have to implement. The header is so simple that it can be nested inside the layout. + + +== The View + +Having discovered the main layout, you can remove those parts from the mock-up. This leaves you with the actual Teams view, which looks like this: + +image:images/view.png[width=800] + +It's a good practice to look for common user interface design patterns. In this view, the most obvious one is the _Master-Detail_ pattern. On the left, there's a list of teams from which the user can select. When the user selects a team, its details appear on the right. + +From the mock-up it's unclear what should happen if there are no teams in the list, or no team is selected. You should determine this before you start implementing. + + +=== Team Selection Panel + +The master-part of the master-detail is the team selection panel, shown here: + +image:images/view-master.png[width=200] + +At the top, there's a header containing the name of the Teams view, a button for creating new teams and a text field for filtering the list of teams. + +The list of teams consists of panels -- one for each team. Each team panel contains the name and description of the team. The team panel is a UI component you _may_ have to implement, depending on how you choose to implement the list. + +You select a team by clicking on the panel. From the mock-up it's unclear whether keyboard navigation should be supported or not. It's also unclear whether the team selection panel is resizable or not, although the lack of a splitter indicates it has a fixed width. These are things you should know before you start implementing, as they affect which components you can use. + +The team selection panel is a UI component you have to implement. The header is so simple that it can be nested inside the panel. + + +=== Team Details Panel + +The detail-part of the master-detail is the team details panel, as shown in this image: + +image:images/view-detail.png[width=800] + +At the top, there is again a header. It contains the name and description of the team, and buttons for editing, sharing and deleting the team. + +To the left, there's a sidebar with two sections: one with general information about the team and another with a list of managers of the team. From the mock-up, it's unclear whether the managers are clickable or not. It's also unclear whether the sidebar is resizable or not, although the lack of a splitter indicates it has a fixed width. Again, these are things you should determine this before you start implementing, as they affect which components you can use. + +To the right, there are tabs that control the contents of the rest of the panel. This indicates that the entire team details panel is in fact a nested layout with three sub-views: _Employees_; _Salaries_; and _Documents_. In fact, the team details panel looks like this: + +image:images/view-detail-panel.png[width=800] + +The team details panel is a UI component you have to implement. The sidebar and header are so simple that they can be nested inside the panel. + + +==== Employees Sub-View + +The first and only sub-view visible in the mock-up is the Employees sub-view: + +image:images/employees-sub-view.png[width=600] + +As with ordinary views, it's a good idea to look for common design patterns. In this case, there's another Master-Detail. At the top, there's a grid of team members. When you select a team member, its details appear in a bottom panel. The splitter between the grid and the bottom panel indicates the bottom panel is resizable. + +From the mock-up, it's unclear what should happen if there are no employees in the grid, or no employee is selected. You should determine this before you start implementing. + +The employee details panel is read-only, which makes it simple to implement. It contains general information about the employee. + +The employees sub-view is a UI component you have to implement. The employee details panel is a UI component you may have to implement, depending on how you choose to implement the sub-view itself. + + +== Generic Components + +You've split the mock-up into a layout, a view, a nested layout and a sub-view. Additionally, you've identified some panels that you need to implement. The final step before you can start to write code is to identify any generic components that are re-used in multiple places. In this mock-up, there are at least two. + +The first generic component is the person panel: + +image:images/person-panel.png[width=300] + +It's used both in the list of managers and in the employees sub-view. It contains the person's picture or avatar, their name, and their title. From the mock-up, you can see that the panel is smaller in the list and larger in the sub-view. + +The second generic component is the item with icon: + +image:images/items.png[width=300] + +This is used both in the team summary and in the employees sub-view. From the mock-up it's unclear whether the phone number and email address should be clickable. You should decide this before you start implementing. + +As you begin to implement the view, you may discover more generic components. This is especially the case with Flow, as laying out components in an imperative way is rather verbose. diff --git a/articles/building-apps/presentation-layer/view-structure/flow-implementation.adoc b/articles/building-apps/presentation-layer/view-structure/flow-implementation.adoc new file mode 100644 index 0000000000..9bd7bd007c --- /dev/null +++ b/articles/building-apps/presentation-layer/view-structure/flow-implementation.adoc @@ -0,0 +1,7 @@ +--- +title: View Implementation with Flow +description: TODO write description +order: 30 +--- + +// TODO Write me! diff --git a/articles/building-apps/presentation-layer/view-structure/images/application.png b/articles/building-apps/presentation-layer/view-structure/images/application.png new file mode 100644 index 0000000000..61bd149a13 Binary files /dev/null and b/articles/building-apps/presentation-layer/view-structure/images/application.png differ diff --git a/articles/building-apps/presentation-layer/view-structure/images/employees-sub-view.png b/articles/building-apps/presentation-layer/view-structure/images/employees-sub-view.png new file mode 100644 index 0000000000..e0a3dd2e99 Binary files /dev/null and b/articles/building-apps/presentation-layer/view-structure/images/employees-sub-view.png differ diff --git a/articles/building-apps/presentation-layer/view-structure/images/items.png b/articles/building-apps/presentation-layer/view-structure/images/items.png new file mode 100644 index 0000000000..c33f1e0e0f Binary files /dev/null and b/articles/building-apps/presentation-layer/view-structure/images/items.png differ diff --git a/articles/building-apps/presentation-layer/view-structure/images/main-layout.png b/articles/building-apps/presentation-layer/view-structure/images/main-layout.png new file mode 100644 index 0000000000..f3a83b9063 Binary files /dev/null and b/articles/building-apps/presentation-layer/view-structure/images/main-layout.png differ diff --git a/articles/building-apps/presentation-layer/view-structure/images/no-employee-selection.png b/articles/building-apps/presentation-layer/view-structure/images/no-employee-selection.png new file mode 100644 index 0000000000..2dc1e3accc Binary files /dev/null and b/articles/building-apps/presentation-layer/view-structure/images/no-employee-selection.png differ diff --git a/articles/building-apps/presentation-layer/view-structure/images/no-selection.png b/articles/building-apps/presentation-layer/view-structure/images/no-selection.png new file mode 100644 index 0000000000..a5fe50c7cd Binary files /dev/null and b/articles/building-apps/presentation-layer/view-structure/images/no-selection.png differ diff --git a/articles/building-apps/presentation-layer/view-structure/images/person-panel.png b/articles/building-apps/presentation-layer/view-structure/images/person-panel.png new file mode 100644 index 0000000000..f123a17c2c Binary files /dev/null and b/articles/building-apps/presentation-layer/view-structure/images/person-panel.png differ diff --git a/articles/building-apps/presentation-layer/view-structure/images/view-detail-panel.png b/articles/building-apps/presentation-layer/view-structure/images/view-detail-panel.png new file mode 100644 index 0000000000..0079eeb6f7 Binary files /dev/null and b/articles/building-apps/presentation-layer/view-structure/images/view-detail-panel.png differ diff --git a/articles/building-apps/presentation-layer/view-structure/images/view-detail.png b/articles/building-apps/presentation-layer/view-structure/images/view-detail.png new file mode 100644 index 0000000000..c93958dce2 Binary files /dev/null and b/articles/building-apps/presentation-layer/view-structure/images/view-detail.png differ diff --git a/articles/building-apps/presentation-layer/view-structure/images/view-master.png b/articles/building-apps/presentation-layer/view-structure/images/view-master.png new file mode 100644 index 0000000000..555c2002be Binary files /dev/null and b/articles/building-apps/presentation-layer/view-structure/images/view-master.png differ diff --git a/articles/building-apps/presentation-layer/view-structure/images/view.png b/articles/building-apps/presentation-layer/view-structure/images/view.png new file mode 100644 index 0000000000..cf0baaaceb Binary files /dev/null and b/articles/building-apps/presentation-layer/view-structure/images/view.png differ diff --git a/articles/building-apps/presentation-layer/view-structure/index.adoc b/articles/building-apps/presentation-layer/view-structure/index.adoc new file mode 100644 index 0000000000..580a3afbb1 --- /dev/null +++ b/articles/building-apps/presentation-layer/view-structure/index.adoc @@ -0,0 +1,5 @@ +--- +title: View Structure +description: TODO write description +order: 10 +--- \ No newline at end of file diff --git a/articles/building-apps/presentation-layer/view-structure/react-implementation.adoc b/articles/building-apps/presentation-layer/view-structure/react-implementation.adoc new file mode 100644 index 0000000000..29c2676740 --- /dev/null +++ b/articles/building-apps/presentation-layer/view-structure/react-implementation.adoc @@ -0,0 +1,7 @@ +--- +title: View Implementation with React +description: TODO write description +order: 40 +--- + +// TODO Write me! diff --git a/articles/building-apps/presentation-layer/view-structure/url-design.adoc b/articles/building-apps/presentation-layer/view-structure/url-design.adoc new file mode 100644 index 0000000000..6db33c8916 --- /dev/null +++ b/articles/building-apps/presentation-layer/view-structure/url-design.adoc @@ -0,0 +1,158 @@ +--- +title: URL Design +description: TODO write description +order: 20 +--- + +// Notes from Gerald: +// +// String IDs in URLs +// - is there maybe a more practical example, how to use NanoId? would you save it along the primary key in the DB? if not: how do you convert it forth and back, in a map? what happens with that map if the application restarts? + + += URL Design + +The <> page explained how to split this mock-up into UI components: + +image::images/application.png[] + +This page explains how to design the URL of the mock-up based on how a user is intended to navigate the view. The main focus is on deciding what _view state_ to store in the URL, as path or query parameters. + +To understand why this is important, suppose you're working with an application, and want one of your colleagues to look at some part of it. You could ask them to open the application and scan it for something you want to show them. However, if the relevant state was stored in the URL, you could instead send that to them. The colleague would then only need to click the link to see the same view as you -- after authenticating. + + +== Path Parameters + +Path parameters are part of the URL path: `/view/[parameter1]/[parameter2]`. Use path parameters for navigating to a specific view, or a sub-view within a view. + +In the fictional application, the root path of the _Teams_ view is `/teams`. If you navigate to this path, you get this: + +image::images/no-selection.png[] + +The first path parameter is the ID of the team: `/teams/[teamId]`. For example, the team _Research and Development (R&D)_ could have a path like, `/teams/8zguABh6u`. + +It's quite common to use incrementing long integers as database primary keys. If you use such a key in the URL, for example `/teams/1`, it's easy for an attacker to deduce that there may also be a `/teams/2`, a `/teams/3` and so on. Even if you've secured the application so that only authorized users can access regardless of whether they know the URL or not, the URL is exposing internal information. This information can be used to attack the application in other ways. Instead, use a random ID such as `/teams/8zguABh6u`. Internally, you can still use long incrementing integers for primary and foreign keys, as they're faster than strings. + +The Teams view has three sub-views, visible as tabs: _Employees_; _Salaries_; and _Documents_. The selected tab is the second path parameter: `/teams/[teamId]/[tab]`. For example, the sub-views could have the following paths: + +- Employees: `/teams/8zguABh6u/employees` +- Salaries: `/teams/8zguABh6u/salaries` +- Documents: `/teams/8zguABh6u/documents` + +Because the Employees tab should be selected by default, the path `/teams/8zguABh6u` should be considered an alias of `/teams/8zguABh6u/employees`. If you navigate to either of these paths, you get this: + +image::images/no-employee-selection.png[] + +The Employees sub-view allows you to select an employee to view their details. This selection is the third path parameter and it's only valid when the Employees tab is selected: `/teams/[teamId]/employees/[employeeId]`. For example, _Cody Fisher_ could have the path `/teams/8zguABh6u/employees/zxPIVBqJ2`. If you navigate to this path, you end up with the state of the original mock-up: + +image::images/application.png[] + + +== Query Parameters + +Query parameters are added to the end of a URL after a question mark (i.e., the `?`). You can specify multiple query parameters by separating them with ampersands (i.e., `&`): `/view?param1=foo¶m2=bar`. Use query parameters if you want to be able to share or bookmark a view while it's in a specific state. Such a state could, for example, be a particular sort order or filter. + +You don't have to use query parameters unless there is a clear benefit to do so. Looking at the mock-up, the view is already quite usable without query parameters. It still has some candidates which you'll look at next, for the sake of demonstration. + +The Teams view has a text field for filtering the list of teams. This is the first candidate for a query parameter: `/teams/8zguABh6u?search=foobar` + +The Employees sub-view has a grid that can be sorted by clicking at the column headers. This is the second candidate for a query parameter: `/teams/8zguABh6u/employees/?orderEmployeesBy=firstName,lastName` + +You can have query parameters and path parameters for both the view and the sub-view at the same time. For example, look at this URL: + +[source] +---- +/teams/8zguABh6u/employees/zxPIVBqJ2?search=research&orderEmployeesBy=firstName,lastName +---- + +You can extract the following information: + +- The current view is the Teams view. +- The team whose public ID is `8zguABh6u` is selected. +- The Employees sub-view is visible. +- The employee whose public ID is `zxPIVBqJ2` is selected. +- The team list is filtered using the search term `research`. +- The employee grid is sorted first by first name, then by last name. + + +== String IDs in URLs + +The examples above used random strings as public IDs. When you generate a random ID, you should pay attention to its usability. Since the ID is public, people may want to copy and paste it, either on its own or as a part of the URL. That means the ID must be URL-friendly, short and easy to copy. + +An easy way of generating random string IDs in Java is to use Universally Unique IDentifiers (UUID). Java has a [classname]`UUID` class in the [packagename]`java.util` package. You can use it to generate random UUIDs by calling [methodname]`UUID.randomUUID()`. However, there is a problem with using UUIDs as public IDs in URLs. + +Try to select this UUID by double-clicking on it: + +[source] +---- +78f98876-b150-4e08-8d7e-41bb5e0f7e72 +---- + +You probably selected only a part of the ID instead of the whole string. This is because of the hyphens. You can fix that by either removing the hyphens or replacing them with underscores. Try to select these IDs by double-clicking on them: + +[source] +---- +78f98876_b150_4e08_8d7e_41bb5e0f7e72 +78f98876b1504e088d7e41bb5e0f7e72 +---- + +You should now be able to select the whole string. However, the string is still quite long and when used as URL parameters, the URL becomes quite long too: + +[source] +---- +https://myapp.example.com/teams/78f98876b1504e088d7e41bb5e0f7e72/employees/5191cfa1823e40858b0f0e10ce50c28e +---- + +This is not a user friendly URL. You can copy-paste it, but it's difficult to read. Telling it to somebody verbally is much more difficult. + +A better alternative for public random string IDs is _NanoId_. NanoIds allow you to pick both the length and the alphabet. By default, the length is 21 characters and the character set allows numbers (0-9), letters (A-Z and a-z), and the hyphen and underscore (i.e., `-` and `_`). + +If you use 9-character NanoIds generated from an character set consisting of only numbers, uppercase and lowercase letters, you'll get a URL like this: + +[source] +---- +https://myapp.example.com/teams/aftD6ZXp6/employees/m4wqAas1Q +---- + +This is shorter and more user friendly. + +NanoId has been implemented in different programming languages, among others https://github.com/aventrix/jnanoid[Java] and https://github.com/ai/nanoid[JavaScript]. + + +== Back Button Behavior + +Once you're ready with the URL design, there's one more thing to consider: the browser's back button. + +When you navigate to a URL in a web browser, a new entry is pushed to the browser's history stack. When you click the back button, the browser goes back one step in the history stack. You can continue to click the button until you reach the bottom of the stack. + +When a user uses a web application, the application itself handles the navigation in response to the user's actions. For example, in the mock-up application, the user would not modify the URL to select a team or a tab. Rather, the user would click on the team and the tab. The application would then be responsible for modifying the URL. + +When a web application updates the URL, it can do it in two ways: either by pushing new entries to the history stack; or by replacing the current entry in the history stack. This affects the behavior of the browser's back button. + +Consider the following scenario: the user opens the application, selects a team, sorts the employee grid and then selects an employee. If every URL change was pushed to the history stack, the history stack would look like the following, sorted from oldest to newest: + +- `/teams` +- `/teams?search=research` +- `/teams/aftD6ZXp6?search=research` +- `/teams/aftD6ZXp6?search=research&orderEmployeesBy=lastName` +- `/teams/aftD6ZXp6/employees/zxPIVBqJ2?search=research&orderEmployeesBy=lastName` + +Given this stack, if the user then clicked once the back button, they would end up with a sorted employee grid without a selection. Another click would take the user to the same employee grid, but with its default sort order. A third click would deselect the team and show the search results. A final click would show the list of all teams, without any filters applied. This would not be what the user expected; it would be a bad user experience. + +Since the search fields has no extra button, the query is executed as soon as the user stops typing. If the user is typing slowly, you may end up with entries like the following in the history stack: + +- `/teams?search=re` +- `/teams?search=resea` +- `/teams?search=research` + +If the user now clicked the back button, they would be confused. It would be an even worse user experience. + +A good general practice is to push new entries to the history stack whenever the path of the URL changes, and replace the current entry whenever query parameters change. With this behavior, the use case described earlier would result in the following history stack, sorted from oldest to newest: + +- `/teams?search=research` +- `/teams/aftD6ZXp6?search=research&orderEmployeesBy=lastName` +- `/teams/aftD6ZXp6/employees/zxPIVBqJ2?search=research&orderEmployeesBy=lastName` + +If the user clicked the back button now, they would end up with a sorted employee grid without a selection. Another click would deselect the team and show the search results. + +As with all good practices, there may be exceptions. What's important here is to consider back button behavior when designing the view URL and be sure it makes sense.