preface

Since working, I have seen more than N interfaces.

That interface, call a hundred flowers bloom, strange shape, how to write all have.

Maybe the company doesn’t have a set of back-end coding conventions.

Let me share my “years” of interface writing experience.

Return data format

When docking with the front end, we generally return three pieces of data to the front end, as shown in the following code:

@Data
public class Response<T> {

    private int code;

    private String message;

    private T data;
}
Copy the code
  • Code: indicates the interface status Code
    • This parameter is returned when the interface request is returned successfully. For example, 0 is returned, indicating success. -1 is returned, indicating failure
    • Cooperate withGlobal exception status codeOutput, easy for the front-end logic judgment processing, through the status code, it is also convenient to locate interface exceptions.
  • Message: error Message
    • Return “successful” if the request is successful.
    • If a request fails, an error message is returned for front-end interconnection
  • Data: defined as a normal type, compatible with multiple return Data types and returning Data required by the interface.

Controller

There are interfaces, input parameters, input parameters, Map a shuttle.

There are interfaces, Domain objects, from beginning to end.

Some interfaces, without comments, return an “R” parameter.

Horizontal batch: what trifle?

Look at the code:

// Junk code
@PostMapping("/add")
public R add(Map map) {
   return R.success(service.add(map));
}

/ / advice
@PostMapping("/add")
public R<UserAddVO> add(@Valid @RequestBody UserAddDTO dto) {
  return R.success(service.add(dto))
} 
Copy the code
  • Remove the Map entry parameter

    • You are advised to use Json or form-data to specify the input parameter.
    • Define it internallyThe suffix refs, such as example code DTO (Data Transfer Object),Specify the input parameter value, as well astypeFacilitates subsequent maintenance.
  • Rename R (optional). Naming conventions are recommended. You can call it Response, KResponse, Result, etc

  • Declare R return type: UserAddVO as shown in the code, and return parameter suffix as shown in example code: VO (View Object)

  • Valid parameter verification (optional). Using the @VALID annotation reduces some parameter verification code.

Java is a strongly typed language. Don’t write it as a weakly typed language. Otherwise, if the new person takes over the project, he shall not greet your family.

AppService

When we first started learning Java, most of our online tutorials taught us to define an AService interface and then write an AServiceImpl implementation class. The following code looks like this:

public interface AService {
  void a(a);
}

public class AServiceImpl {
  
  @Override
  public void a (a) {
    // dosomething...}}Copy the code

Maybe that’s a good way to write it. Interface oriented programming.

However, after several years of writing code, I found that this kind of AService interface is a bit redundant, and the following business code basically has no possibility of extension, which is useless.

Later I changed to direct AService class come, much more comfortable. As follows:

/ / advice
public class AService {
  public void a (a) {
    // dosomething...}}Copy the code

In retrospect, I wrote AppService in the title.

Why do I write it this way?

The first is limited by the traditional three-tier architecture, Controller, Service, Repository/Mapper. With the continuous development of business, the code of Service layer will gradually expand, which is not easy to maintain.

The second is to learn the DDD architecture pattern, from the new name ApplicationService, short for AppService.

To put it simply, AppService is the facade, that is, in the AppService layer, it does not do complex business logic, but only do resource arrangement, and the specific business details are dealt with in the corresponding Service.

Let’s get straight to the code:

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class BAppService {

    private final BRepository bRepository;
    / / ellipsis.. service

    public Boolean do(Long userId) {
        
	User user = userRepository.findByUserId(userId);
        if (user.isMan()) {
           throws new RuntimeException("Men can't do it!");
        }
      
      	List<GirlFriend> girlFriends = girlFriendRepository.findByUserId(userId);
        for (GirlFriend girlFriend : girlFriends) { 
            // Business processing..
        }
      
      	B b = new B();
      	// omit some sets
        bRepository.save(b);
        return true; }}Copy the code

As BAppService shows, a lot of complex business logic is done in the DO method.

I’m sure you’ve seen hundreds or thousands of lines of Service business logic code.

AppService focuses on too many business details and violates the principle of single responsibility. Writing a single test at the same time is also difficult to test.

Let’s optimize the Service code.

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class BAppService {

    private final BRepository bRepository;
    / / ellipsis.. service

    public Boolean do(Long userId) {
        
	/*User user = userRepository.findByUserId(userId); If (user.isman ()) {throws new RuntimeException(" Men can't do it!" ); } * /
      
      	// Replace it with the following code:
      	womenUseService.check(user);
      
      	/*List
      
        girlFriends = girlFriendRepository.findByUserId(userId); For (GirlFriend: girlFriends) {// Business deal with.. } * /
      
      	
      	// Replace it with the following code:
      	girlFriendDoService.do(userId);
      
      	B b = new B();
      	// omit some sets
        bRepository.save(b);
        return true; }}Copy the code

In the BAppService class, we extract the womenUseService, girlFriendDoService class, and pull the complex business logic out of it. In the do method, Only calls to womenUseService and girlFriendDoService are performed.

For more details on ApplicationService, please refer to Teng Yun’s article on Backend Development Practices: Domain-Driven Design (DDD) Coding Practices

Repository / Mapper

RM: RM: RM: RM: RM: RM: RM: RM: RM: RM: RM: RM: RM: RM: RM

User user = userRepository.findByUserId(userId);
if (user == null) {
   throws new RuntimeException("User does not exist")
}
Copy the code

CService checks the existence of the user every time it needs to query the user based on the userId.

In fact, we added another layer of Dao on the basis of RM to isolate the persistence framework. For example, we extracted a UserDao class, which can be reused directly later.

@Repository
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserDao {
  
   private final UserRepository userRepository;
  
   public User findByUserId(a) {
      User user = userRepository.findByUserId(userId);
      if (user == null) {
         throws new RuntimeException("User does not exist")
      }
      return user; }}Copy the code

Writing interface documentation

And the front end, the front end is mainly to see the interface document.

Here I use Swagger as an example:

@API (tags = "User related Interface ")	 // Add swagger interface document annotation
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserController { 

  	@PostMapping("/add")
    @apiOperation (" Add user ")      // Add swagger interface document annotation
    public R<UserAddVO> add(@Valid @RequestBody UserAddDTO dto) {
      return R.success(service.add(dto))
    } 
}
Copy the code

Here we annotate the corresponding interface Controller class with @API to indicate that this is a collection of user-specific interfaces.

Then, in each Controller method, the interface, annotate @apiOperation to indicate what that interface does, as shown in the code above, adding a user interface.

@Data
public class UserAddVO {
  
 	@ ApiModelProperty (" name ") // Add swagger annotation to indicate field meaning
  	private String name;
  
	@apiModelProperty (value = "age ", example = "20") // Add swagger annotation to indicate field meaning
  	private int age;
		
    / /... Omit the other
}
Copy the code

After the interface is written, you need to add a swagger annotation to the return parameters to indicate what these fields mean.

By default, the swagger document will generate a default value according to the type. As shown in the code above, the age field will generate a default value of 0. If you need to define a value, you can use the example attribute to indicate it. This facilitates interface debugging without having to manually construct the data each time.

So the front end can be docked. Otherwise, guess what?

self-test

After writing the interface, we also need to test ourselves, do not rush to test, docking front end

Can you guarantee that your code is bug-free without testing it yourself?

Generally through Postman self-testing, of course, you can also write your own integration tests to ensure interface quality

For details on how to integrate testing, see my other article Java Integration Testing

The last

If it is the old project code, or suggest first do not move, accident is not responsible ~

But if you write new code, write it as shown above, and congratulations, you’ve already surpassed many, many programmers

Above is all content, hope can help you ~

If there is anything wrong, welcome to point out, we exchange and learn together.

Thanks for reading, see you next time.