What is the difference between an enterprise development project and a self-study project?

foreword

Long time no see everyone! It has not been updated in the past few months, because I have been submitting resumes for interviews for internships since the spring recruitment, and then entered the job, and finally succeeded in becoming a semi-finished back-end trainee. There are too many things I want to say. Let me stand
here As a school intern, combined with my experience of writing code in the enterprise development environment in recent months, I will share with my friends who are about to work or are still looking forward to work——What is the difference between enterprise-level projects and online self-study projects? What is the difference
insert image description here
The company's mainstream technology stack:
SpringBoot+Dubbo+Flink+Kalfk+MyBatisPlus+Mysql+Redis+Seata+MongoDB+ES+React+blockchain+artificial intelligence

1. Complex business logic

There is often a qa environment (quality assurance) between the production and development environments. When I connected to the database of the qa environment on the first day I entered the company, my first impression was: "Damn, what's in this list There are too many fields, right?
A large number of fields correspond to complex business logic, and entity classes no longer have a single attribute, and more are different entities combined with each other, returning the results of multi-party effects: for some business
scenarios The Wrapper of MybatisPlus has certain limitations, and we need to manually encapsulate the table lookup method by ourselves. For example, queries need to use weird functions, multi-table queries, join queries, subqueries, etc...
Now I will give you two Mappers that I wrote during the development process to show you, appetizing:
1. Flexible use of various functions:
insert image description here
2. Complex condition judgment:
insert image description here
3. Complex sub-table join query
insert image description here

2. Strict parameter verification

Enterprises will have uniform requirements for the format of special data, so in the front-end controller (Controller), it is necessary to perform corresponding parameter verification on the received parameters
1. Verification of time type parameters
insert image description here
2. Verification of mobile phone number type parameters
Some people will ask if the data front end of the mobile phone number will help us verify it? Why does the backend have to work repeatedly? In fact, this is because we need to ensure the successful execution of test cases, avoid getting the token after using interface testing tools such as PostMan, and then bypassing the frontend and requesting incorrect data to the backend. End server
insert image description here
3. Verification of paging parameters
The size of paging should also be controlled within a reasonable range, too large or unreasonable will lead to unexpected errors. For example, if a very large pageSize parameter is passed, and the amount of data in the library is large, it will cause a large amount of data to be detected in one request. Going back and forth several times will take up a lot of server bandwidth, and eventually the server will hang~
insert image description here

3. Production environment requirements for interface performance

1. Circular library search is not allowed
Let’s take a well-known example—Riji Takeaway, which should be regarded as a representative of self-study projects. Let’s take a look at the writing method of one of the interfaces:

    @GetMapping("/page")
    public R<Page> page(int page, int pageSize, String name) {
    
    
        log.info("查询的名称是:{}", name);

        //分页构造器
        Page<Dish> dishPage = new Page<>(page, pageSize);

        Page<DishDto> dishDtoPage = new Page<>();
        //条件构造器
        LambdaQueryWrapper<Dish> dishLqw = new LambdaQueryWrapper<>();
        //模糊查询,将前端输入的名字和Dish表中的name进行模糊查询并添加name不为空的条件
        dishLqw.like(name != null, Dish::getName, name);
        dishLqw.orderByDesc(Dish::getUpdateTime);

        //调用Service查询
        dishService.page(dishPage, dishLqw);

        BeanUtils.copyProperties(dishPage, dishDtoPage, "records");
        List<Dish> records = dishPage.getRecords();

        List<DishDto> dtoList = records.stream().map((temp) ->{
    
    
            DishDto dishDto = new DishDto();

            //再次拷贝,将普通属性拷贝给dishDto
            BeanUtils.copyProperties(temp, dishDto);

            Long categoryId = temp.getCategoryId();  //拿到分类id
            // 拿到分类对象,根据id查到对象
            Category category = categoryService.getById(categoryId);

            if(category!=null) {
    
     //避免出现空指针
                //通过分类对象拿到name
                String categoryName = category.getName();
                //把name设置给dishDto
                dishDto.setCategoryName(categoryName);//设置dishDto中CategoryName的值
            }
            return dishDto;
        }).collect(Collectors.toList());

        dishDtoPage.setRecords(dtoList);

        return R.success(dishDtoPage);
    }

Careful students will definitely find that in our stream, a library search operation (i/o) is performed for each element in the stream.
Category category = categoryService.getById(categoryId);
When the amount of data is very small, the performance of the interface may not be affected, but if the data in the stream is large, such an interface must take a very long time, and it will definitely not be used in the production environment. This is one of the biggest differences between enterprise and self-study.
Therefore, in order to avoid such a situation, we will try our best to reduce the frequency of checking the database, which is similar to the following (very similar scene, and it is also the first interface I joined): In a word, it can be checked out all at
insert image description here
once . Try not to check it one at a time.
If you see a circular interactive database in the interface in the future, then the interface is likely to be a cancer. 2.
Multi-threaded asynchronous processing
In fact, some scenarios do not have strong requirements for synchronization, and synchronous processing will increase a lot. Invalid wait time. For some "places that don't need to wait for the result to return", asynchronous processing can be performed boldly.
To give an example, the current task is to push a message to the owner based on a batch of unpaid bills, and update the reminder status every time the push is completed. Both update and push have a lot of pre-data to prepare, and the call chain of the push business is very long. If it is a single-threaded method, the method executes the interface from top to bottom, and it takes about 5-6s. Without an asynchronous call mechanism, the caller of the method cannot do anything during the push time, and other codes have pressed the pause button. But when I use the thread pool to asynchronously push messages with one click and update the status, a lot of waiting time will be reduced, and the time consumption of the interface will also be reduced to less than one second!
insert image description here
It can be seen that for some special scenarios, it is really important to use asynchronous or synchronous methods. And multithreading is just one of the means for us to achieve asynchronous

4. Coding Standards

4.1 Streaming programming

The feature of sub-databases based on a single microservice has resulted in a lot of strongly associated data not being in one database, so we have lost many opportunities for multi-table joint query.
On the contrary, it is more about processing, and assembling multiple data from different microservice interfaces, and streaming programming will not be disabled in this process.
Learning and getting used to streaming programming can really make your own development feel like a fish in water. I would like to call it the lore of streaming writing + Lambda expressions . It not only simplifies business code but also makes developers' thinking clearer.
Let me give you a few examples below. A few scenarios where I used streaming development and lambda expressions after I joined the job:
1. Filter and deduplicate List

List<Long> communityIds = transactionList.stream()
   .map(TransactionCommunityVO::getPayeeCommunityId)
   .filter(ObjectUtils::isNotEmpty).distinct()
   .collect(Collectors.toList());

2. MyBatisPlus uses lambda expressions to perform "selection fuzzy combination query"

LambdaQueryChainWrapper<Device> deviceLambdaQueryChainWrapper = iDeviceService.lambdaQuery();
    if (ObjectUtils.isNotEmpty(deviceQuery2.getFuzz())) {
    
    
     deviceLambdaQueryChainWrapper.and(x -> {
    
    
       x.like(Device::getName, deviceQuery2.getFuzz()).or().like(Device::getCode1, deviceQuery2.getFuzz());
      });
}

3. Collect "the value of a certain field and the number of occurrences of this field" in the List in the form of <kv> map by grouping

  Map<Long, Long> idCountMap = deviceVo2s.stream()
  .filter(index -> ObjectUtils.isNotEmpty(index.getOwnerId()))
  .collect(Collectors.groupingBy(DeviceVo2::getOwnerId, Collectors.counting()));

4. Process strings in the stream

        list= list.stream().map(x -> {
    
    
            String[] strings = new String[x.length];
            for (int i = 0; i < x.length; i++) {
    
    
                if (null == x[i]) {
    
    
                    x[i] = "";
                }
                strings[i] = x[i].trim();
            }
            return strings;
        }).collect(Collectors.toList());

5. Collect data with specified conditions in the flow

Map<String, String> map2 = list4.stream().filter(temp -> "男".equals(temp.split("-")[1]))
                .collect(Collectors.toMap(
                        temp -> temp.split("-")[0]
                        ,
                        temp -> temp.split("-")[2]));

6. Conditions for collecting data

List<ChargingPileOrder> orders = taskItemAction.queryUserIdChargingWithUseChargingPileCounts(publisherIds);
       Map<Long, String> resultMap = orders.stream()
              //同一个用户可能使用不同充电桩并且使用相同次数的,查出来的数据已经按照时间拍好了序,
              //流的作用,就是取同一用户下不同充电桩使用次数相同情况之中的最近使用的那条数据
            .collect(Collectors.groupingBy(ChargingPileOrder::getPublisherId,
                 Collectors.collectingAndThen(
                      Collectors.maxBy(Comparator.comparing(ChargingPileOrder::getCreateTime)),
                           optional -> optional.map(ChargingPileOrder::getChargingPileName).orElse(""))));

4.2 Reusability requirements

A good interface is not limited to the existing functions, but more to achieve upward compatibility. The level of reusability largely determines the length of business code. In business development, it is often seen that "xxx times usage" is displayed in the upper left corner of an interface at the bottom layer, and such an interface has high reusability.
As for how to improve code reusability, here are two places that I feel deeply.
1. Reduce unnecessary parameter passing:
methods with a large number of parameters that need to be passed are usually difficult to read. We can encapsulate all the parameters into an object to complete the transfer of the object, just get it directly when it is needed, which is also conducive to error tracking. Many developers have an impact on system efficiency because of too many layers of object wrappers. However, compared to the benefits it brings, we would rather do packaging. After all, "encapsulation" is also one of the basic characteristics of OOP, and "each object performs as few (and simple) functions as possible" is also a basic principle of OOP.
2. Decoupling as much as possible:
"Separate the variable parts from the immutable parts" is the third principle of object-oriented design. If we use the inheritance reuse technology, we can define the immutable part in the abstract base class, and its subclasses can implement the variable part. The immutable part does not need to be defined repeatedly, and it is easy to maintain. If we use the reuse technology of object composition, we can define the immutable part, and the variable part can be implemented by different components, and dynamically configured at runtime according to the needs. This way, we have more time to focus on the variable parts. For the object composition technology, each component only completes a relatively small function, the coupling between them is relatively loose, and the reuse rate is high. Through combination, new functions can be obtained.
3. Write less fixed conditions in the underlying method:
especially in the Mapper or Wapper method, try to write as few unnecessary fixed-value query conditions as possible, such as limited status, type, etc., and leave a changeable transfer parameter to go up compatible.

4.3 Use of enumeration classes

When I first learned enumeration a year ago, someone asked me what do you think of the enumeration class. I said that he is a class that defines specifications
insert image description here
. Now it is more obvious in enterprise development. Many times, some fields in the database often have many values. For example, the value of this field in the table statusmay be 0, 1, 2 or 3... Different values ​​represent different states of this piece of data.
insert image description here
In the process of data processing, do not use xxx=0, xxx=2 when encountering restrictions on the state... Instead, this is not only xxx=StatusEnum.ONLINE.getValue()more intuitive for other developers to understand at a glance, but also achieves a certain meaning Decoupling on !
Put the common ones in one place, isn't this scenario very similar to a configuration file? I think IOC relied on DOM4J and XML at the beginning

4.4 Use of query class

Enterprise-level projects often have very complex requirements. For example, a query interface may receive many parameters. These field parameters come down from the Controller layer and are passed down layer by layer. This may lead to a bad phenomenon—the implementation method receives Too many parameters!
like this:

PageInfo<Device> getDeviceList(Integer current, Integer size, List<Long> ownerIds, List<DeviceTypeEnum> typeList, 
String code1, String code2, List<Integer> providerList, List<IsDeletedType> isDeletedList, List<Long> communityIdList,
 String nameLike, List<Long> physicalCommunityIdList, List<Integer> statusList, List<Long> parentIdList, List<Long>
  idList, List<String> areaPathList, List<String> sortList, Sort.Direction direction, List<String> communityPathList, 
  List<DeviceSubTypeEnum> deviceSubTypeEnumList, String code1Like, String code2Like);

Isn't this formal parameter too long~ This way of writing looks "regular", but it is very eye-catching to call, and it takes a long time to debug if a parameter is passed to the wrong position!
insert image description here
Therefore, in order to solve such problems, we use the query class to query (the query class is to encapsulate the attribute field in the entity class, and operate the attribute through the set and get methods), which will make the interface method look much simpler. And greatly reduce the probability of error, like this:
insert image description here

4.5 Restful style

I used to talk about RestFul-style applications when I was learning SpringMVC by myself. At that time, I didn’t come into contact with enterprise-level projects and felt that this style of things was irrelevant, but in fact it is not. It is very important for interfaces to follow such style specifications!
The client uses four HTTP verbs to operate on the server-side resources to realize "state transformation of the presentation layer". In enterprise-level applications, there are often dozens of Controllers in a module. When you browse among them, you will find that you will understand the Controller method after only looking at the request method (post/get/delete/update) and the request path. general function. The RestFul style is undoubtedly an excellent development specification!

4.6 Notes

Last but not least!
Remember to write code and remember to write comments. The so-called comment is definitely not a comment, but a statement on the key place.
For example:
1. Description of the specific function of the interface
2. Description of formal parameter passing
3. Description of the type and content of the return value
4. Description of the attribute field in the entity class
5. Description of the special use of the common field

Guess you like

Origin blog.csdn.net/weixin_57535055/article/details/130920187