Scilim

blog

About

LendARead

2023-06-20

Table of contents

Introduction

The goal of this project was building a fullstack webapp for the class 72.38 Web application project. The design was based upon Domain Driven Design (DDD) of Eric Evans, with special focus on good practises and layer separation. In terms of technologies the project is built with Java, Spring and Postgresql.

The objective of the web application is to create a community of readers, where all of them can upload their books and request loans from each other. The aim is to allow readers to access books that would not normally be easily accessible, creating like a decentralized library.

For example, let's imagine the user Julian, who enjoys reading philosophy in multiple languages. Thanks to other readers with German heritage (for example), he can borrow a philosophy book from a German author that he could not have read by going to a library in Argentina.

Part of the goal of the project was also migrating different parts of the webapp after they had been built. Initially the persistence layer was implemented in raw postgresql without an object relational model (ORM). Once this was fully built, we had to make a migration to an ORM while keeping the production database intact. The latter being maintained througout the whole life of the project. The other big migration was converting the whole presentation layer, initially written in jsp, to a javascript framework of our choice. In doing so it was also necessary to transform the backend to a REST api, see LeandARead2

Project through Domain Driven Design

Figure 1: Project Structure

In terms of structure, every implementation of DDD has its own interpretation. As can be seen in Figure 1 we depict the structure proposed in the Blue Book and how it's implemented in the project.

Persistence layer

Starting from the last layer, we have persistence which can be found in the folder persistence and the interfaces in persistenceinterfaces. This layer encapsulates all the logic relating to database through multiple repositories. In DDD terminology a repository can be understood as an abstraction of a collection, the goal being to offer an interface to the business layer to make operations with the models without regard to them being on disk, memory or wherever. This allowing the change of dbms or implementation (as we did from raw sql to an ORM) without propagating changes to the upper layers.

Domain logic layer

The next layer we have is business logic or domain logic which alse has its own maven project under the name services and the interfaces in interfaces. This layer comes to represent all the logic specific to the domain of the webapp, in our case it has all the logic of lendings, returns, reviews, and so on. The encapsulation allows to abstract the business logic from any particular technology of persistence (i.e. database) and presentation (i.e. web, mobile, desktop), thus allowing for greater robustness and resilience of the system through technology migrations.

Presentation and Application layers

Finally, the presentation layer and application layer are both within the webapp module of our project. The controllers work by making use of the facade that the services layer provides, working like a glue between the services and the presentation layer. Moreover, this enabled us to have no logic whatsoever in the presentation layer, only the jsp, css and javascript for interactivity.

Models

A good question at this point is how do these layers interact? the answer is models, also its own maven project. The idea of models, also known as infraestructure in DDD, is to represent the objects that will be persisted by the persistence layer, managed by the business layer, and displayed by the presentation layer.

Project structure

The project structure is built upon five different modules (each of which is its own maven project) that all link to a parent project with a shared pom.xml for all dependencies. This has numerous advantages for large scale projects. For instance all the dependencies versions are centralised and only included in those layers actually needed.

Along the same lines, the use of multiple maven projects allows us to define different dependencies between the layers, and further encapsulate each layer. For instance, the presentation layer pom.xml is defined such that it cannot access any class defined in the persistence layer (it doesnot have the persitenceinterface dependency).

Finally, the use of interfaces for every service and repository is to make use of Spring's dependency injection. Again, making only use of the interfaces for the services and repositories further detachs the layers, as no repository or service is never instantiated explicitly, there is no coupling of actual implementation across layers.

Domain logic Layer - interfaces

.
├── README.md
├── interfaces
│ ├── pom.xml
│ └── src
│     └── main
│         └── java
│             └── ar
│                 └── edu
│                     └── itba
│                         └── paw
│                             ├── exceptions
│                             │   ├── AssetInstanceBorrowException.java
│                             │   ├── AssetInstanceNotFoundException.java
│                             │   ├── DayOutOfRangeException.java
│                             │   ├── ImageNotFoundException.java
│                             │   ├── InternalErrorException.java
│                             │   ├── LendingCompletionUnsuccessfulException.java
│                             │   ├── UnauthorizedUserException.java
│                             │   └── UserNotFoundException.java
│                             └── interfaces
│                                 ├── AssetAvailabilityService.java
│                                 ├── AssetExistanceService.java
│                                 ├── AssetInstanceReviewsService.java
│                                 ├── AssetInstanceService.java
│                                 ├── EmailService.java
│                                 ├── ISBNCheckerService.java
│                                 ├── ImageService.java
│                                 ├── LanguagesService.java
│                                 ├── LibraryAPIService.java
│                                 ├── LocationsService.java
│                                 ├── UserAssetInstanceService.java
│                                 ├── UserReviewsService.java
│                                 └── UserService.java

Here we have all the different interfaces available to the application layer to interact with the domain layer without needing to be aware of the actual service implementations, nor those of the exceptions that will be handled.

Models

├── models
│ ├── pom.xml
│ └── src
│     └── main
│         └── java
│             └── ar
│                 └── edu
│                     └── itba
│                         └── paw
│                             └── models
│                                 ├── assetExistanceContext
│                                 │   ├── factories
│                                 │   │   └── BookFactory.java
│                                 │   └── implementations
│                                 │       ├── AssetInstanceImpl.java
│                                 │       ├── AssetInstanceReview.java
│                                 │       ├── BookImpl.java
│                                 │       ├── LanguageImpl.java
│                                 │       └── PhysicalCondition.java
│                                 ├── assetLendingContext
│                                 │   └── implementations
│                                 │       ├── AssetState.java
│                                 │       ├── LendingImpl.java
│                                 │       └── LendingState.java
│                                 ├── miscellaneous
│                                 │   └── ImageImpl.java
│                                 ├── userContext
│                                 │   └── implementations
│                                 │       ├── Behaviour.java
│                                 │       ├── LocationImpl.java
│                                 │       ├── PasswordResetTokenImpl.java
│                                 │       ├── UserImpl.java
│                                 │       └── UserReview.java
│                                 └── viewsContext
│                                     ├── implementations
│                                     │   ├── PageImpl.java
│                                     │   ├── PagingImpl.java
│                                     │   ├── SearchQueryImpl.java
│                                     │   ├── Sort.java
│                                     │   └── SortDirection.java
│                                     └── interfaces
│                                         ├── AbstractPage.java
│                                         ├── Page.java
│                                         ├── PageUserAssets.java
│                                         └── SearchQuery.java

In the models module we can appreciate the different contexts that were defined for the project. These categorization comes directly from domain driven design and the ubiquitous language that was developed through the project.

Persistence Layer - implementations

├── persistence
│ ├── persistence.iml
│ ├── pom.xml
│ └── src
│     ├── main
│     │   ├── java
│     │   │   └── ar
│     │   │       └── edu
│     │   │           └── itba
│     │   │               └── paw
│     │   │                   └── persistence
│     │   │                       ├── AssetAvailabilityDaoJpa.java
│     │   │                       ├── AssetDaoJpa.java
│     │   │                       ├── AssetInstanceDaoJpa.java
│     │   │                       ├── AssetInstanceReviewsDaoJpa.java
│     │   │                       ├── ImagesDaoJpa.java
│     │   │                       ├── LanguagesDaoJpa.java
│     │   │                       ├── LocationsDaoJpaImpl.java
│     │   │                       ├── UserAssetsDaoJpa.java
│     │   │                       ├── UserDaoJpa.java
│     │   │                       └── UserReviewsDaoJpa.java
│     │   └── resources
│     │       ├── alters.sql
│     │       ├── languages.sql
│     │       └── schema.sql
│     └── test
│         ├── java
│         │   └── ar
│         │       └── edu
│         │           └── itba
│         │               └── paw
│         │                   └── persistence
│         │                       ├── AssetAvailabilityDaoTest.java
│         │                       ├── AssetDaoImplTest.java
│         │                       ├── AssetInstanceDaoTest.java
│         │                       ├── AssetInstanceReviewTest.java
│         │                       ├── LocationDaoTest.java
│         │                       ├── UserJdbcDaoTest.java
│         │                       └── config
│         │                           └── TestConfig.java
│         └── resources
│             ├── hsqldb.sql
│             └── inserts.sql

Persistence Layer - interfaces

├── persistenceinterfaces
│ ├── pom.xml
│ └── src
│     └── main
│         └── java
│             └── ar
│                 └── itba
│                     └── edu
│                         └── paw
│                             ├── exceptions
│                             │   └── BookAlreadyExistException.java
│                             └── persistenceinterfaces
│                                 ├── AssetAvailabilityDao.java
│                                 ├── AssetDao.java
│                                 ├── AssetInstanceDao.java
│                                 ├── AssetInstanceReviewsDao.java
│                                 ├── ImagesDao.java
│                                 ├── LanguageDao.java
│                                 ├── LocationDao.java
│                                 ├── UserAssetsDao.java
│                                 ├── UserDao.java
│                                 └── UserReviewsDao.java
├── pom.xml

Business Layer

├── services
│ ├── pom.xml
│ └── src
│     ├── main
│     │   ├── java
│     │   │   └── ar
│     │   │       └── edu
│     │   │           └── itba
│     │   │               └── paw
│     │   │                   └── services
│     │   │                       ├── AssetAvailabilityServiceImpl.java
│     │   │                       ├── AssetExistanceServiceImpl.java
│     │   │                       ├── AssetInstanceReviewsServiceImpl.java
│     │   │                       ├── AssetInstanceServiceImpl.java
│     │   │                       ├── EmailServiceImpl.java
│     │   │                       ├── ISBNCheckerServiceImpl.java
│     │   │                       ├── ImageServiceImpl.java
│     │   │                       ├── LanguagesServiceImpl.java
│     │   │                       ├── LibraryAPIServiceImpl.java
│     │   │                       ├── LocationsServiceImpl.java
│     │   │                       ├── UserAssetInstanceServiceImpl.java
│     │   │                       ├── UserReviewsServiceImpl.java
│     │   │                       └── UserServiceImpl.java
│     │   ├── resources
│     │   │   └── emails
│     │   │       ├── ForgotPasswordEmailTemplate.html
│     │   │       ├── borrowerEmailTemplate.html
│     │   │       ├── canceledEmail.html
│     │   │       ├── lenderEmailTemplate.html
│     │   │       ├── notifyLenderNewLending.html
│     │   │       ├── notifyLenderReturn.html
│     │   │       ├── rejectedEmail.html
│     │   │       ├── reviewBorrowerEmail.html
│     │   │       └── reviewLenderEmail.html
│     │   └── static
│     │       ├── defaultBookImage.png
│     │       └── noImage.jpeg
│     └── test
│         └── java
│             └── ar
│                 └── edu
│                     └── itba
│                         └── paw
│                             └── services
│                                 ├── AssetAvailabilityServiceImplTest.java
│                                 ├── AssetInstanceServiceImplTest.java
│                                 ├── ImageServiceImplTest.java
│                                 ├── LibraryAPIServiceImplTest.java
│                                 ├── UserReviewsServiceImplTest.java
│                                 └── UserServiceImplTest.java

Application layer

└── webapp
    ├── pom.xml
    └── src
        └── main
            ├── java
            │ └── ar
            │     └── edu
            │         └── itba
            │             └── paw
            │                 └── webapp
            │                     ├── auth
            │                     │   ├── AuthSuccessHandler.java
            │                     │   ├── BorrowerReviewVoter.java
            │                     │   ├── BorrowerViewVoter.java
            │                     │   ├── ChangeAssetStatusVoter.java
            │                     │   ├── DeleteAssetVoter.java
            │                     │   ├── LenderReviewVoter.java
            │                     │   ├── LenderViewOwnerVoter.java
            │                     │   ├── PawUserDetails.java
            │                     │   └── PawUserDetailsService.java
            │                     ├── config
            │                     │   ├── EmailConfig.java
            │                     │   ├── WebAuthConfig.java
            │                     │   └── WebConfig.java
            │                     ├── controller
            │                     │   ├── AccessDeniedController.java
            │                     │   ├── AddAssetViewController.java
            │                     │   ├── AssetViewController.java
            │                     │   ├── ErrorsControllerAdvice.java
            │                     │   ├── ForgotPasswordController.java
            │                     │   ├── ISBNCheckerController.java
            │                     │   ├── ImagesController.java
            │                     │   ├── IndexViewController.java
            │                     │   ├── LocationsController.java
            │                     │   ├── LoginLogOutViewController.java
            │                     │   ├── RegisterViewController.java
            │                     │   ├── ReviewsController.java
            │                     │   ├── UserAssetDetailsController.java
            │                     │   ├── UserDataControllerAdvice.java
            │                     │   ├── UserHomeViewController.java
            │                     │   ├── UserProfileViewController.java
            │                     │   └── UserRolesController.java
            │                     ├── form
            │                     │   ├── AddAssetForm.java
            │                     │   ├── AssetInstanceForm.java
            │                     │   ├── BorrowAssetForm.java
            │                     │   ├── BorrowerReviewForm.java
            │                     │   ├── ChangePasswordForm.java
            │                     │   ├── ChangeProfilePicForm.java
            │                     │   ├── EmailForm.java
            │                     │   ├── LenderReviewForm.java
            │                     │   ├── LocationForm.java
            │                     │   ├── RegisterForm.java
            │                     │   ├── SearchFilterSortForm.java
            │                     │   ├── SnackbarControl.java
            │                     │   ├── UserAssetFilterSortForm.java
            │                     │   └── annotations
            │                     │       ├── implementations
            │                     │       │   ├── ChangePasswordRepeatPasswordImpl.java
            │                     │       │   ├── EmailExistenceImpl.java
            │                     │       │   ├── EmailImpl.java
            │                     │       │   ├── EmailNotExistenceImpl.java
            │                     │       │   ├── ImageValidatorImpl.java
            │                     │       │   ├── IsbnImpl.java
            │                     │       │   ├── NotEmptyImpl.java
            │                     │       │   ├── RegisterRepeatPasswordImpl.java
            │                     │       │   └── ValidTokenImpl.java
            │                     │       └── interfaces
            │                     │           ├── Email.java
            │                     │           ├── EmailExistence.java
            │                     │           ├── EmailNotExistence.java
            │                     │           ├── Image.java
            │                     │           ├── Isbn.java
            │                     │           ├── NotEmpty.java
            │                     │           ├── RepeatPassword.java
            │                     │           └── ValidToken.java
            │                     └── miscellaneous
            │                         ├── CustomMultipartFile.java
            │                         └── FormFactoryAddAssetView.java

Presentation Layer

└── webapp
    ├── pom.xml
    └── src
        └── main
            └── webapp
                ├── WEB-INF
                │ ├── jsp
                │ │   ├── components
                │ │   │   ├── addAssetModal.jsp
                │ │   │   ├── bookCard.jsp
                │ │   │   ├── bookCardPlaceholders.jsp
                │ │   │   ├── calendar.jsp
                │ │   │   ├── changePictureModal.jsp
                │ │   │   ├── filterItem.jsp
                │ │   │   ├── indexBookCard.jsp
                │ │   │   ├── locationCard.jsp
                │ │   │   ├── locationModal.jsp
                │ │   │   ├── modal.jsp
                │ │   │   ├── navBar.jsp
                │ │   │   ├── paginationButton.jsp
                │ │   │   ├── ratingGeneral.jsp
                │ │   │   ├── reviewCard.jsp
                │ │   │   ├── reviewCardProfile.jsp
                │ │   │   ├── simpleCalendar.jsp
                │ │   │   ├── snackbarComponent.jsp
                │ │   │   └── userInfoSmall.jsp
                │ │   └── views
                │ │       ├── 403View.jsp
                │ │       ├── addAssetView.jsp
                │ │       ├── assetView.jsp
                │ │       ├── borrowAssetView.jsp
                │ │       ├── changePassword.jsp
                │ │       ├── discoveryView.jsp
                │ │       ├── editAssetInstance.jsp
                │ │       ├── error.jsp
                │ │       ├── forgotPassword.jsp
                │ │       ├── index.jsp
                │ │       ├── locations.jsp
                │ │       ├── loginView.jsp
                │ │       ├── registerView.jsp
                │ │       ├── reviewBorrower.jsp
                │ │       ├── reviewLender.jsp
                │ │       ├── reviewSubmited.jsp
                │ │       ├── userHomeAssetDetail
                │ │       │   ├── borrowedBookOptions.jsp
                │ │       │   ├── cancelModal.jsp
                │ │       │   ├── changeReservabilityModal.jsp
                │ │       │   ├── changeStatusModal.jsp
                │ │       │   ├── confirmModal.jsp
                │ │       │   ├── deleteBookModal.jsp
                │ │       │   ├── lendedBookOptions.jsp
                │ │       │   ├── lendingBookDetails.jsp
                │ │       │   ├── myBookOptions.jsp
                │ │       │   ├── rejectModal.jsp
                │ │       │   ├── returnModal.jsp
                │ │       │   └── userBookDetails.jsp
                │ │       ├── userHomeView
                │ │       │   ├── borrowedBooksTable.jsp
                │ │       │   ├── filterButton.jsp
                │ │       │   ├── lendedBooksTable.jsp
                │ │       │   ├── myBooksTable.jsp
                │ │       │   ├── paginationButtons.jsp
                │ │       │   ├── sortButton.jsp
                │ │       │   └── userHomeView.jsp
                │ │       └── userProfileView.jsp
                │ └── web.xml
                └── static
                    ├── css
                    │ ├── 403View.css
                    │ ├── addAssetView.css
                    │ ├── assetView.css
                    │ ├── bookCard.css
                    │ ├── bookCardIndex.css
                    │ ├── changeAsset.css
                    │ ├── discovery.css
                    │ ├── index.css
                    │ ├── lendingBookDetails.css
                    │ ├── login.css
                    │ ├── main.css
                    │ ├── modal.css
                    │ ├── navBar.css
                    │ ├── neoBookCard.css
                    │ ├── profileReviewCard.css
                    │ ├── searchBar.css
                    │ ├── starRating.css
                    │ ├── userHomeView.css
                    │ └── userProfile.css
                    ├── images
                    │ ├── broken_lendaread.png
                    │ ├── favicon-claro-bg.ico
                    │ ├── favicon-claro-bg.png
                    │ ├── favicon-claro.ico
                    │ ├── favicon-claro.png
                    │ ├── favicon-oscuro-bg.ico
                    │ ├── favicon-oscuro-bg.png
                    │ ├── favicon-oscuro.ico
                    │ ├── favicon-oscuro.png
                    │ ├── indexPhoto.svg
                    │ ├── loading-gif.gif
                    │ ├── login-bg.jpg
                    │ ├── logo-claro.png
                    │ ├── logo-oscuro.png
                    │ ├── no_image_placeholder.jpg
                    │ ├── placeholderLoader.png
                    │ ├── profilePicPlaceholder.png
                    │ └── user-placeholder.jpeg
                    └── javaScript
                        ├── addAssetForm.js
                        ├── addAssetView.js
                        ├── assetView.js
                        ├── bookCard.js
                        ├── cardSetting.js
                        ├── discovery.js
                        ├── map.js
                        ├── reviewBorrower.js
                        ├── reviewLender.js
                        ├── topbar.js
                        ├── userHomeView.js
                        ├── userProfile.js
                        └── utils.js