API design best practices

Parameter design

  • Fixed parameters are of basic type
  • Too many parameters (8) Object parameters are recommended
  • Interface Reserved extension objects to facilitate post-processing of personalized requirements
  • Parameters need to consider whether certain fields involving money are self-increasing and easy to guess after exposure

Return value design

  • Provides a generic interface for invoking methods that fail successfully
  • Implement Serializable override ID
  • Avoiding enumerations
  • The structure of the body
      "code": 200."model": ""."msg": ""
    Copy the code
  • Keep your object design simple
  • The server side handles the general logic well, and the front end only does the display, for example: the tag display, the server side abstracts the tag copy and type, and the front end makes the customized style display according to the type, and the tag can be extended at will
  • Convergence in the same scope
  • Middle structure often need to combine the basic services to obtain the information encapsulated a fat object, so we need to DDD model abstract objects, divided into different scopes, such as commodity information, only need to show the original price of the price for the front and the current price, so we need to put the logic of the two prices converge to these two fields, There is no need for the front-end to do other business processing
  • The logic control should converge
  • For the same field that represents different values in different business scenarios, add a processing class for this field for logical convergence
  • Layering according to business logic
  • According to the business of the returned object, the parent-child relationship of the object is determined. The universal attribute belongs to the parent class, and the personality attribute belongs to the subclass object

Exception handling

  • Enumeration defines error codes and supports configurable error copywriting
  • Annotations surround the way exceptions are handled, logically convergent


  • For services that cause data changes, do a good job of preventing repeated invocation
  • Shortest path principle
  • The lazy load mode is used when loading
  • For intermediate data, you need to pass the build Context in multiple layers of method calls and place it in a ThreadLocal to allow access anywhere in the thread, but be aware of memory leaks (1, thread pool 2). Active release after use)
  • The general decision logic converges to the static class, which is convenient for the later decision logic modification and omission
  • For the downstream interface call, in line with the first skeptical attitude, notice to determine empty

Nonfunctional features

  • performance
    • Traffic limiting degradation. The interface that does not return data has no impact on users can adopt the circuit breaker policy. If the data returned is abstract core and non-core, the non-core services can be degraded when the traffic is too heavy
      • sentinal
      • A semaphore
      • hytrixs
    • Low-level parallel call
      • Concurrent invocations
      • RxJava
    • Comprehensive services in multiple scenarios, underlying strength control
      • By designing control bits, different control bits are allocated according to different business scenarios to reduce unnecessary calls
  • security
    • Login mode, user authentication
    • Sensitive data desensitization, user’s personal privacy data desensitization collection number in the middle of the four placeholder display
  • scalability
    • Interface upgrade
      • Think about compatibility issues when the interface is upgraded, whether it involves a lot of changes from outside calls,
    • Personalized needs
  • Robustness,
    • Fault-tolerant handling of downstream service invocations

      • Custom annotations surround
         public @interface ResultWrapper {
             DefaultResultEnum defaultObj(a) default DefaultResultEnum.OBJECT;
      Copy the code
      • Define return types to avoid null Pointers
        LIST(1."list", Collections.EMPTY_LIST),
        MAP(2."map", Collections.EMPTY_MAP),
        SET(3."set", Collections.EMPTY_SET),
        BOOLEAN(6."boolean", Boolean.FALSE),
        Copy the code
    • Unified downstream services return objects

      Result<T> implements Serializable {
                /** * Return code */
                private int code = 200;
                /** * Error message */
                private String msg;
                /** return model */
                private T model;
              public boolean isSuccess(a){
                  return 200==code;
      Copy the code
    • Overall interface fault tolerance

      • Custom annotations surround
         @Around(value = "@annotation(serviceWrapper)", argNames = "joinPoint,serviceWrapper")
                        public Object doAround(ProceedingJoinPoint joinPoint, ServiceWrapper serviceWrapper) throws Throwable {
                            final long begin = System.currentTimeMillis();
                            Object object = serviceWrapper.isReturnBooleanType() ? false : null;
                            try {
                                object = joinPoint.proceed();
                                return object;
                            }  catch (Exception e) {
                                LogControl.printLogWarn("exception", Throwables.getStackTraceAsString(e),JSON.toJSONString(joinPoint.getArgs()));
                            return object;
        Copy the code
    • Ease of query

      • Key node log printing, code design needs to predict possible problems, and the key information needed to query the problem
        • Override the toString method to focus on printing process data. If the interface exposes data, it is recommended to keep only key information such as the primary key ID
        • These are some of the factors I take into account when designing an interface. If you have good suggestions, please add your comments