diff --git a/articles/building-apps/application-layer/persistence/flyway.adoc b/articles/building-apps/application-layer/persistence/flyway.adoc index 015f3cd8d1..d094236f15 100644 --- a/articles/building-apps/application-layer/persistence/flyway.adoc +++ b/articles/building-apps/application-layer/persistence/flyway.adoc @@ -4,6 +4,7 @@ description: How to manage your relational database schema with Flyway. order: 30 --- + = Flyway Whenever you store data in a relational database, you have to manage the database schema in some way. When the application is first installed, you have to create the entire database schema. When new features are deployed, you have to update the database schema. You may need to add new tables to the database. You may need to add new columns to existing tables. You may need to move data from one table to another one, and delete the old one. @@ -13,6 +14,7 @@ Some object-relational mapping tools, like Hibernate, can generate the initial s [NOTE] On this page, you'll learn enough about Flyway to get started using it in your Vaadin applications. Because Flyway has more features than presented here, you should also read the https://documentation.red-gate.com/flyway[Flyway Documentation]. + == Migrations Flyway is based on the concept of _migrations_. A migration is a script that performs some changes on your database. Every migration is versioned. As you implement new features, you add new migrations to the project. Flyway keeps track of which migrations have been applied to the database in a separate table. This table includes a checksum of every migration script. @@ -23,6 +25,7 @@ Versioned migrations should not change after they have been applied. When Flyway In addition to versioned migrations, Flyway also supports repeatable migrations. These migrations can change after they have been applied, and are automatically re-applied after every change. Repeatable migrations are always applied after the versioned migrations. + == Writing Migrations You can write migrations in multiple languages, including Java, but the most common one is ordinary SQL. The migration scripts should follow a specific naming pattern. Versioned migrations start with an uppercase `V`, followed by a version number, two underscores `\__`, a description, and the suffix `.sql`. For example, a migration could be named `V1__initial_schema_setup.sql`. @@ -33,6 +36,7 @@ You should store your SQL scripts in the `src/main/resources/db/migration` direc For information about writing migrations in other languages than SQL, see the https://documentation.red-gate.com/flyway[Flyway Documentation]. + == Migrating on Application Startup Spring Boot has built-in support for Flyway. If the `org.flywaydb:flyway-core` module is on the classpath, Flyway is automatically executed on application startup. @@ -56,12 +60,12 @@ Spring Boot declares the modules in its parent POM, so you don't have to look up org.postgresql - postgresql + postgresql /* <1> */ runtime org.flywaydb - flyway-database-postgresql + flyway-database-postgresql /* <2> */ runtime @@ -77,6 +81,7 @@ To make Flyway use its own data source, set the `spring.flyway.[url,user,passwor For more information, see the https://docs.spring.io/spring-boot/how-to/data-initialization.html#howto.data-initialization.migration-tool.flyway[Spring Boot Documentation]. + == Migrating with Maven Sometimes, you may want to run the Flyway migrations as a separate build step. For example, you may not want to make the DDL user credentials available to the application itself for security reasons. Flyway has a Maven plugin that allows you to run the migration scripts as a part of your build chain. diff --git a/articles/building-apps/application-layer/persistence/repositories/index.adoc b/articles/building-apps/application-layer/persistence/repositories/index.adoc index c37070687e..86374f8789 100644 --- a/articles/building-apps/application-layer/persistence/repositories/index.adoc +++ b/articles/building-apps/application-layer/persistence/repositories/index.adoc @@ -4,10 +4,12 @@ description: How to use repositories to store and fetch data. order: 10 --- + = Repositories The _repository_ was originally introduced as one of the building blocks of tactical Domain-Driven Design, but has since then become common in all business applications, mainly thanks to https://spring.io/projects/spring-data[Spring Data]. A repository is a persistent container of entities that attempts to abstract away the underlying data storage mechanism. At its minimum, it provides methods for basic CRUD operations: Creating, Retrieving, Updating, and Deleting entities. + == Collection Oriented Collection oriented repositories try to mimic an in-memory collection, such as `Map` or `List`. Once an entity has been added to the repository, any changes made to it are automatically persisted until it has been deleted from the repository. In other words, there is no need for a `save` or `update` method. @@ -54,6 +56,7 @@ repository.remove(CustomerId.of("XRxY2r9P")); Collection oriented repositories can be quite difficult to implement. The repository implementation would have to know when an entity has been changed, so that it can write it to the underlying storage. Handling transactions and errors would also be non-trivial. This is a telling example of the underlying storage mechanism leaking into the repository abstraction. + == Persistence Oriented Persistence oriented repositories do not try to hide the fact that the data has to be written to, and read from, some kind of external storage. They have separate methods for inserting, updating, and deleting the entity. If the repository is able to deduce whether any given entity has been persisted or not, the `insert` and `update` methods can be combined into a single `save` method. No changes to an entity are ever written to the storage without an explicit call to `save`. @@ -107,6 +110,7 @@ Persistence oriented repositories are easier to implement than collection orient *Unless you have a good reason for choosing collection-based repositories, you should use persistence oriented repositories in your Vaadin applications.* + == Query Methods Although retrieving an entity by its ID is an important operation, it is not enough in most business applications. You need to be able to retrieve more than one entity at the same time, based on different criteria. If the dataset is big, you need to be able to split it into smaller pages and load them one at a time. @@ -157,6 +161,7 @@ The challenge with this approach is that it is difficult, but not impossible, to You can find examples of how to implement specification queries on the <> and <> documentation pages. + == Query Objects Query specifications are useful when you are interested in fetching whole entities. However, you often need to write queries that only include a small part of the entity. For example, if you are building a customer list view that only shows the customers' names and email addresses, there is no point in fetching the complete Customer-entity. The repository now looks like this: @@ -201,6 +206,7 @@ If you know the Command Query Responsibility Segregation (CQRS) architectural pa // TODO Add link to using CQRS in Vaadin app, when that page has been written sometime in the future. + == Building section_outline::[] \ No newline at end of file diff --git a/articles/building-apps/application-layer/persistence/repositories/pluggable.adoc b/articles/building-apps/application-layer/persistence/repositories/pluggable.adoc index 8ea4e80e22..34e6e00e80 100644 --- a/articles/building-apps/application-layer/persistence/repositories/pluggable.adoc +++ b/articles/building-apps/application-layer/persistence/repositories/pluggable.adoc @@ -4,6 +4,7 @@ description: How to support multiple persistence solutions with a single SPI. order: 40 --- + = Pluggable Persistence Most business applications only need a single persistence solution that remains for the lifetime of the application. In these cases, there is no point in hiding the persistence solution below a large abstraction layer. However, there are applications where you have to do this. For instance, some customers may want to use your application with a local database, while others want to use it with a remote web service. @@ -21,13 +22,15 @@ You can read more about pluggable implementations on the <<{articles}/building-a [NOTE] This page describes how to design entities and repositories as an SPI for other modules to implement. It assumes you have read the <> documentation page. + == Entities You have three options when it comes to designing entities for your persistence SPI: POJO:s, records, or interfaces. -=== POJO:s -POJO:s, or Plain Old Java Objects, are just that: ordinary Java objects. They may be mutable or immutable. They may contain business logic, or only act as data structures. They have to expose all the data that a repository needs to persist them. The easiest way of doing this is to expose the data through public getter methods, for example like this: +=== POJOs + +POJOs, or Plain Old Java Objects, are just that: ordinary Java objects. They may be mutable or immutable. They may contain business logic, or only act as data structures. They have to expose all the data that a repository needs to persist them. The easiest way of doing this is to expose the data through public getter methods, for example like this: [source,java] ---- @@ -130,6 +133,7 @@ Regardless of how you implement your POJO:s, pay close attention to validation. // TODO Add links to validation. + === Records Java records are immutable, initialized through the constructor, and expose all their fields through public getter methods. This makes your SPI simpler, as there are less moving parts. It also makes your entities more like Data Transfer Objects (DTO), than entities. The `Project` POJO from the earlier example would look like this as a record: @@ -169,6 +173,7 @@ Records are useful if you want to use the latest Java features to implement your // TODO Should write an article about how to do this. + === Interfaces If you want to give the repository implementation full control over your entities, you can define them as interfaces. For example, a `Product` entity interface could look like this: @@ -253,6 +258,7 @@ product.setDescription("Bar"); The factory implementation would have to be a Spring managed bean, so that it can be injected into your services, or wherever it is needed. + ==== Read-Only Entity Interfaces If you declare entity interfaces that are read-only, leave out the `get` prefix from the getter methods. This makes them much easier to combine with Java records. Consider the following interface: @@ -273,6 +279,7 @@ You can implement it using a Java record like this: public record ProductRecord(Long productId, String name, String description) implements Product {} ---- + == Repositories The repositories are themselves a part of your SPI. Therefore, they are either interfaces, or abstract classes.