Giter VIP home page Giter VIP logo

spring-boot-style-guide's Introduction

Spring Boot Style Guide

An opinionated guide on developing web applications with Spring Boot. Inspired by Airbnb JavaScript Style Guide.

Join the chat at https://gitter.im/helpermethod/spring-boot-style-guide License

Table of Contents

Dependency Injection

  • Use constructor injection. Avoid field injection.

Why? Constructor injection makes dependencies explicit and forces you to provide all mandatory dependencies when creating instances of your component.

// bad
public class PersonService {
    @AutoWired
    private PersonRepository personRepositoy;
}

// good
public class PersonService {
    private final PersonRepository personRepository;

    // if the class has only one constructor, @Autowired can be omitted
    public PersonService(PersonRepository personRepository) {
        this.personRepository = personRepository;
    }
}    
  • Avoid single implementation interfaces.

Why? A class already exposes an interface: its public members. Adding an identical interface definition makes the code harder to navigate and violates YAGNI.

What about testing? Earlier mocking frameworks were only capable of mocking interfaces. Recent frameworks like Mockito can also mock classes.

// bad
public interface PersonService {
    List<Person> getPersons();
}

public class PersonServiceImpl implements PersonService {
    public List<Person> getPersons() {
        // more code
    }
}

// good
public class PersonService {
    public List<Person> getPersons() {
        // more code
    }
}

⬆ back to top

Controllers

  • Use @RestController when providing a RESTful API.
// bad
@Controller
public class PersonController {
    @ResponseBody
    @GetMapping("/persons/{id}")
    public Person show(@PathVariable long id) {
        // more code
    }
}

// good
@RestController
public class PersonController {
    @GetMapping("/persons/{id}")
    public Person show(@PathVariable long id) {
        // more code
    }
}
  • Use @GetMapping, @PostMapping etc. instead of @RequestMapping.
// bad
@RestController
public class PersonController {
    @RequestMapping(method = RequestMethod.GET, value = "/persons/{id}")
    public Person show(@PathVariable long id) {
        // more code
    }
}

// good
@RestController
public class PersonController {
    @GetMapping("/persons/{id}")
    public Person show(@PathVariable long id) {
        // more code
    }
}
  • Simplify your controller keeping it thin

Why? To avoid SRP violations;

Where should I put my business logic? Keep the bussines logic encapsulated into your services or specialized classes;

// bad
@PostMapping("/users")
public ResponseEntity<Void> postNewUser(@RequestBody UserRequest userRequest) {
    if (userRequest.isLessThanEighteenYearsOld()) {
        throw new IllegalArgumentException("Sorry, only users greater or equal than 18 years old.");
    }

    if (!userRequest.hasJob()) {
        throw new IllegalArgumentException("Sorry, only users working.");
    }

    if (!this.userService.hasUsernameAvailable(userRequest.getUsername())) {
        throw new IllegalArgumentException(String.format("Sorry, [%s] is not an available username.", userRequest.getUsername()));
    }

    this.userService.createNewUser(userRequest);

    return ResponseEntity.status(HttpStatus.CREATED).build();
}

// good
@PostMapping("/users")
public ResponseEntity<Void> postNewUser(@RequestBody UserRequest userRequest) {
    this.userService.createNewUser(userRequest);
    return ResponseEntity.status(HttpStatus.CREATED).build();
}

public class UserService {

    // variables declaration

    public void createNewUser(UserRequest userRequest) {
        this.validateNewUser(userRequest);
        UserEntity newUserEntity = this.userMapper.mapToEntity(userRequest);
        this.userRepository.save(newUserEntity);
    }

    private void validateNewUser(UserRequest userRequest) {
        // business validations
    }
}

⬆ back to top

Serialization

  • Do not map your JSON objects to JavaBeans.

Why? JavaBeans are mutable and split object construction across multiple calls.

// bad
public class Person {
    private String firstname;
    private String lastname;

    public void setFirstname() {
        this.firstname = firstname;
    }

    public String getFirstname() {
        return firstname;
    }

    public void setLastname() {
        this.lastname = lastname;
    }

    public String getLastname() {
        return lastname;
    }
}

// good
public class Person {
    private final String firstname;
    private final String lastname;

    // requires your code to be compiled with a Java 8 compliant compiler 
    // with the -parameter flag turned on
    // as of Spring Boot 2.0 or higher, this is the default
    @JsonCreator
    public Person(String firstname, String lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
    }

    public String getFirstname() {
        return firstname;
    }

    public String getLastname() {
        return lastname;
    }
}

// best
public class Person {
    private final String firstname;
    private final String lastname;

    // if the class has a only one constructor, @JsonCreator can be omitted
    public Person(String firstname, String lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
    }

    public String getFirstname() {
        return firstname;
    }

    public String getLastname() {
        return lastname;
    }
}

⬆ back to top

Testing

  • Keep Spring out of your unit tests.
class PersonServiceTests {
    @Test
    void testGetPersons() {
        // given
        PersonRepository personRepository = mock(PersonRepository.class);
        when(personRepository.findAll()).thenReturn(List.of(new Person("Oliver", "Weiler")));

        PersonService personService = new PersonService(personRepository);

        // when
        List<Person> persons = personService.getPersons();

        // then
        assertThat(persons).extracting(Person::getFirstname, Person::getLastname).containsExactly("Oliver", "Weiler");
    }
}

Why? AssertJ is more actively developed, requires only one static import, and allows you to discover assertions through autocompletion.

// bad
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.empty;

assertThat(persons), is(not(empty())));

// good
import static org.assertj.core.api.Assertions.assertThat;

assertThat(persons).isNotEmpty();

⬆ back to top

spring-boot-style-guide's People

Contributors

boristhuy avatar gabriel-rcpereira avatar gitter-badger avatar helpermethod avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spring-boot-style-guide's Issues

Ideas

  • Use Spring Initializr
  • Do not put business logic into your controllers
  • Use Spring Boot Actuator
  • Use JUnit5
  • Use AssertJ
  • Use @Bean for object construction
  • Put @Beans into @Configuration classes
  • Use @Service for services
  • Use @Autowired in integration tests
  • @Services should be stateless.
  • Use Testcontainers. Do not use H2.
  • Use Hoverfly.

Create table of content

We should create a table of content so that it is easier to navigate to particular style guideline

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.