{"id":17103010,"url":"https://github.com/helpermethod/spring-boot-style-guide","last_synced_at":"2025-04-13T00:42:23.484Z","repository":{"id":43840065,"uuid":"166444816","full_name":"helpermethod/spring-boot-style-guide","owner":"helpermethod","description":"An opinionated guide on developing web applications with Spring Boot.","archived":false,"fork":false,"pushed_at":"2021-09-26T17:26:29.000Z","size":85,"stargazers_count":132,"open_issues_count":3,"forks_count":14,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-04-13T00:42:16.899Z","etag":null,"topics":["best-practices","spring","spring-boot","style-guide"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/helpermethod.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-18T17:07:31.000Z","updated_at":"2025-04-08T22:15:11.000Z","dependencies_parsed_at":"2022-08-28T20:30:56.885Z","dependency_job_id":null,"html_url":"https://github.com/helpermethod/spring-boot-style-guide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/helpermethod%2Fspring-boot-style-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/helpermethod%2Fspring-boot-style-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/helpermethod%2Fspring-boot-style-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/helpermethod%2Fspring-boot-style-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/helpermethod","download_url":"https://codeload.github.com/helpermethod/spring-boot-style-guide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248650417,"owners_count":21139672,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["best-practices","spring","spring-boot","style-guide"],"created_at":"2024-10-14T15:31:03.573Z","updated_at":"2025-04-13T00:42:23.447Z","avatar_url":"https://github.com/helpermethod.png","language":null,"readme":"# Spring Boot Style Guide\n\nAn opinionated guide on developing web applications with Spring Boot. Inspired by [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript).\n\n[![Join the chat at https://gitter.im/helpermethod/spring-boot-style-guide](https://badges.gitter.im/helpermethod/spring-boot-style-guide.svg)](https://gitter.im/helpermethod/spring-boot-style-guide?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/helpermethod/spring-boot-style-guide/master/LICENSE)\n\n## Table of Contents\n\n* [Dependency Injection](#dependency-injection)\n* [Controllers](#controllers)\n* [Serialization](#serialization)\n* [Testing](#testing)\n\n## Dependency Injection\n\n* Use `constructor injection`. Avoid `field injection`.\n\n\u003e Why? Constructor injection makes dependencies explicit and forces you to provide all mandatory dependencies when creating instances of your component.\n\n```java\n// bad\npublic class PersonService {\n    @AutoWired\n    private PersonRepository personRepositoy;\n}\n\n// good\npublic class PersonService {\n    private final PersonRepository personRepository;\n\n    // if the class has only one constructor, @Autowired can be omitted\n    public PersonService(PersonRepository personRepository) {\n        this.personRepository = personRepository;\n    }\n}    \n```\n\n* Avoid single implementation interfaces.\n\n\u003e Why? A class already exposes an interface: its public members. Adding an identical `interface` definition makes the code harder to navigate and violates [YAGNI](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it).\n\u003e\n\u003e What about testing? Earlier mocking frameworks were only capable of mocking interfaces. Recent frameworks like [Mockito](https://site.mockito.org/) can also mock classes. \n\n```java\n// bad\npublic interface PersonService {\n    List\u003cPerson\u003e getPersons();\n}\n\npublic class PersonServiceImpl implements PersonService {\n    public List\u003cPerson\u003e getPersons() {\n        // more code\n    }\n}\n\n// good\npublic class PersonService {\n    public List\u003cPerson\u003e getPersons() {\n        // more code\n    }\n}\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Controllers\n\n* Use `@RestController` when providing a RESTful API.\n\n```java\n// bad\n@Controller\npublic class PersonController {\n    @ResponseBody\n    @GetMapping(\"/persons/{id}\")\n    public Person show(@PathVariable long id) {\n        // more code\n    }\n}\n\n// good\n@RestController\npublic class PersonController {\n    @GetMapping(\"/persons/{id}\")\n    public Person show(@PathVariable long id) {\n        // more code\n    }\n}\n```\n\n* Use `@GetMapping`, `@PostMapping` etc. instead of `@RequestMapping`.\n\n```java\n// bad\n@RestController\npublic class PersonController {\n    @RequestMapping(method = RequestMethod.GET, value = \"/persons/{id}\")\n    public Person show(@PathVariable long id) {\n        // more code\n    }\n}\n\n// good\n@RestController\npublic class PersonController {\n    @GetMapping(\"/persons/{id}\")\n    public Person show(@PathVariable long id) {\n        // more code\n    }\n}\n```\n\n* Simplify your controller keeping it thin\n\n\u003e Why? To avoid [SRP](https://en.wikipedia.org/wiki/Single-responsibility_principle#:~:text=The%20single%2Dresponsibility%20principle%20(SRP,it%20should%20encapsulate%20that%20part.)) violations;\n\n\u003e Where should I put my business logic? Keep the bussines logic [encapsulated](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)) into your services or specialized classes;\n\n```java\n// bad\n@PostMapping(\"/users\")\npublic ResponseEntity\u003cVoid\u003e postNewUser(@RequestBody UserRequest userRequest) {\n    if (userRequest.isLessThanEighteenYearsOld()) {\n        throw new IllegalArgumentException(\"Sorry, only users greater or equal than 18 years old.\");\n    }\n\n    if (!userRequest.hasJob()) {\n        throw new IllegalArgumentException(\"Sorry, only users working.\");\n    }\n\n    if (!this.userService.hasUsernameAvailable(userRequest.getUsername())) {\n        throw new IllegalArgumentException(String.format(\"Sorry, [%s] is not an available username.\", userRequest.getUsername()));\n    }\n\n    this.userService.createNewUser(userRequest);\n\n    return ResponseEntity.status(HttpStatus.CREATED).build();\n}\n\n// good\n@PostMapping(\"/users\")\npublic ResponseEntity\u003cVoid\u003e postNewUser(@RequestBody UserRequest userRequest) {\n    this.userService.createNewUser(userRequest);\n    return ResponseEntity.status(HttpStatus.CREATED).build();\n}\n\npublic class UserService {\n\n    // variables declaration\n\n    public void createNewUser(UserRequest userRequest) {\n        this.validateNewUser(userRequest);\n        UserEntity newUserEntity = this.userMapper.mapToEntity(userRequest);\n        this.userRepository.save(newUserEntity);\n    }\n\n    private void validateNewUser(UserRequest userRequest) {\n        // business validations\n    }\n}\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Serialization\n\n* Do not map your JSON objects to `JavaBeans`.\n\n\u003e Why? JavaBeans are mutable and split object construction across multiple calls.\n\n```java\n// bad\npublic class Person {\n    private String firstname;\n    private String lastname;\n\n    public void setFirstname() {\n        this.firstname = firstname;\n    }\n\n    public String getFirstname() {\n        return firstname;\n    }\n\n    public void setLastname() {\n        this.lastname = lastname;\n    }\n\n    public String getLastname() {\n        return lastname;\n    }\n}\n\n// good\npublic class Person {\n    private final String firstname;\n    private final String lastname;\n\n    // requires your code to be compiled with a Java 8 compliant compiler \n    // with the -parameter flag turned on\n    // as of Spring Boot 2.0 or higher, this is the default\n    @JsonCreator\n    public Person(String firstname, String lastname) {\n        this.firstname = firstname;\n        this.lastname = lastname;\n    }\n\n    public String getFirstname() {\n        return firstname;\n    }\n\n    public String getLastname() {\n        return lastname;\n    }\n}\n\n// best\npublic class Person {\n    private final String firstname;\n    private final String lastname;\n\n    // if the class has a only one constructor, @JsonCreator can be omitted\n    public Person(String firstname, String lastname) {\n        this.firstname = firstname;\n        this.lastname = lastname;\n    }\n\n    public String getFirstname() {\n        return firstname;\n    }\n\n    public String getLastname() {\n        return lastname;\n    }\n}\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Testing\n\n* Keep Spring out of your unit tests.\n\n```java\nclass PersonServiceTests {\n    @Test\n    void testGetPersons() {\n        // given\n        PersonRepository personRepository = mock(PersonRepository.class);\n        when(personRepository.findAll()).thenReturn(List.of(new Person(\"Oliver\", \"Weiler\")));\n\n        PersonService personService = new PersonService(personRepository);\n\n        // when\n        List\u003cPerson\u003e persons = personService.getPersons();\n\n        // then\n        assertThat(persons).extracting(Person::getFirstname, Person::getLastname).containsExactly(\"Oliver\", \"Weiler\");\n    }\n}\n```\n\n* Use [AssertJ](http://joel-costigliola.github.io/assertj/). Avoid [Hamcrest](http://hamcrest.org/).\n\n\u003e Why? `AssertJ` is more actively developed, requires only one static import, and allows you to discover assertions through autocompletion.\n\n```java\n// bad\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.hamcrest.Matchers.is;\nimport static org.hamcrest.Matchers.not;\nimport static org.hamcrest.Matchers.empty;\n\nassertThat(persons), is(not(empty())));\n\n// good\nimport static org.assertj.core.api.Assertions.assertThat;\n\nassertThat(persons).isNotEmpty();\n```\n\n**[⬆ back to top](#table-of-contents)**\n","funding_links":[],"categories":["\u003ca name=\"Not%20Set\"\u003e\u003c/a\u003eNot Set"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhelpermethod%2Fspring-boot-style-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhelpermethod%2Fspring-boot-style-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhelpermethod%2Fspring-boot-style-guide/lists"}