preface

It’s true that apps do most of these things, but what underpins all of these things is doing what the architecture is thinking about.

  • Calling the Network API
  • The page display
  • Local persistence of data
  • Dynamic deployment scheme

The above four points, a little more details are:

  • How to make it easy and secure for business development engineers to call network apis? And then try to make sure that the user has a good experience in every network environment possible?
  • How can the pages be organized to minimize the coupling of business code? Minimize the complexity of the business side’s development interface and increase their efficiency?
  • When there is a need for data to be accessed locally, how can we ensure that the data is properly arranged locally? How can I minimize the performance cost?
  • IOS apps have approval cycles, so how can you show new content to users without releasing a version? How do I fix emergency bugs?

The above points are for apps, and the following are for teams:

  • Collect user data and provide reference for product and operation
  • Reasonably organize business modules developed by all business parties, as well as related basic modules
  • Daily app automatic packaging, provide testing tools for QA engineers

1. Method of architecture design

Every architect is bound to have a methodology, but again, no matter what approach you take, a holistic view, a strong code aesthetic, and a flexible use of various design patterns are bound to run through it.

1.1 Find out which problems to solve and find the necessary and sufficient conditions to solve these problems

You have to know what you’re doing and what the business wants. Not architecture for architecture’s sake, not changing architecture solutions to experience new technologies. It used to be MVC, but now MVVM is popular. If the old MVC is a good architecture and there are no big flaws, don’t tear it down and make it MVVM.

I should also mention that there are times when system-provided functions require additional arguments, such as the read function. And when you turn a page, the current page number is also necessary and sufficient. But for the business side, these conditions can be further reduced.

So read, you have to give it file descriptor, you have to give it buf, you have to give it size. But for the business side, the necessary and sufficient conditions are just file descriptor. Another example is page flipping. The business side does not need to record the current page number. You can expose a loadNextPage method to the business side.

It is important to know what is really necessary and sufficient for the business side! This determines whether your architecture is easy to use. In addition, the fewer parameters you pass, the smaller the coupling, and the less expensive it is for you to replace or upgrade a module.

1.2 Problem classification, divided into modules

Categorization reduces horizontal dependencies.

1.3 Make clear the dependency between the problems, establish a good module communication standard and design modules

The key is to establish a uniform set of communication norms. This step is a good indicator of the architect’s values in software, and while there may be some pros and cons (such as the fat Model and the thin Model), since you’re an architect, you’re rarely going to design something that’s obviously bad, unless the architect isn’t good enough. So this is a window into the output of the architect’s values, from which we can see the quality of the architect.

In addition, it should be noted that a unified set of communication standards must be established, not two sets, not many sets. You stick to your values and don’t waver. If a variety of multifarious norms come out, on the one hand there is unrealistic show-off suspicion, on the other hand will bring follow-up maintenance of disaster.

1.4 Deduce and predict the possible future trend, add new modules if necessary, and record more basic data for future needs

Many architects worth their salt at this point are thinking about where the architecture is going, and what to do next after this round of architecture is done. Although a good architecture is a project that will benefit future generations, it is by no means a once-and-for-all project. Software is alive, and the architecture you build determines whether the software will have a rough or happy life.

1.5 First solve the most basic problems in the dependency relationship, realize the base module, and then stack the whole architecture with the base module

This step is also a step to verify that your previous design is sound, and as you move forward, you will most likely encounter situations where you need to adjust your architecture. This stage must be critical and highly responsible to develop, do not muddle through, find problems in the framework of timely adjustment. Otherwise the cost of subsequent adjustment will be very large.

1.6 Hit points, run unit tests, run performance tests, optimize corresponding places according to the data

You use this data to take credit to your Leader, and you use this data to constantly tweak your architecture. It all boils down to following these principles: top-down design (steps 1,2,3,4), bottom-up implementation (5), measure first, optimize later (6).

2. What makes a good architect?

  • Every day in learning, new technology and new ideas to get started fast, fast understanding, can not do this, you are a coder.

  • Business background, or at least very familiar with the company’s industry or business, you are operations.

  • Familiar with various specifications of software engineering, stepped on numerous pits. Don’t do anything to get what you need done. Don’t believe in quick & Dirty

Failing that, you’d be better off working as an engineer for a competitor.

  • Admit your mistakes as soon as possible, and don’t feel like admitting to them undermines your status as an architect

You can’t do that. Pr works for you.

  • Show off for show off’s sake, and if you can’t do that, you’re a high school coder.

  • Excelsior won’t do that. (I’ve thought about it for a long time, but I still don’t know where you fit in.) .

3. What is a good architecture?

  1. Neat code, clear classification, no common, no core.
  2. With little or no documentation, the business side can get started.
  3. The train of thought and method should be unified, do not multivariate as far as possible.
  4. No horizontal dependencies, no cross-layer access as a last resort.
  5. There are restrictions on the place where the business side should limit, and the flexible place should create conditions for the business side to achieve flexible implementation.
  6. Easy to test, easy to expand.
  7. Maintain a certain amount of advance.
  8. Fewer interfaces and fewer interface parameters.
  9. High performance.

These are my criteria for deciding whether an architecture is a good architecture, in order of importance.

3.1 Neat code, clear classification, nocommon, there is nocore.

Code tidiness is the basic quality of every engineer, let alone how good your solution to solve this problem, how fast the solution, if the code is not neat, everything is useless. Because your code is for others to see, you should see it yourself. If the architecture changes and it happens to be in this place, it’s easy to lose your head. In addition, the broken window theory reminds us that if the code is not neatly classified, the architecture will become more and more chaotic with each extension. The literal meaning of categorization is familiar, but there is another meaning: don’t make a class or a module do two different things. If you have a class or a module that does two different things, on the one hand it is not suitable for future extension, on the other hand it can cause classification difficulties. Don’t do Common Core. I can’t be wrong in saying that these two folders are the most disgusting in every company’s architectural code base. Don’t open a folder like Common, Core, because if you open it, somebody else will make a mess of it, and you’ll end up with Common, Core, and Core. Keep in mind that architecture is constantly growing and changing. Not every growth and every change is yours to make happen. If something is really too small, just create a separate module for it. The key is to order it.

3.2 With little or no documentation, the business side can get started.

Who the hell is going to read the documentation? The business side has their hands full with the product manager. So you want to make your API names as readable as possible, and for iOS, objC is a language that does this extremely well. It doesn’t matter if the function names are longer.

Good function name: - (NSDictionary *)exifDataOfImage:(UIImage *)image atIndexPath:(NSIndexPath *)indexPath; Bad function name: - (id)exifData:(UIImage *)image position:(id)indexPath callback:(id<ErrorDelegate>)delegate; Why is it bad? 1. Do not directly return id or pass in ID. Otherwise, id<protocol> is better than ID. If you can't do that, you need to think about your architecture. 2. To tell the business what to send, for example, to send Image, write ofImage. If you want to pass a position, say IndexPath, not position. 3. There is no reason to pass in the delegate as a parameter, and there will never be a case where you have to. And the delegate parameter is not a necessary and sufficient condition for the problem this function is trying to solve. If you find that you have to do this, it's because of an architecture problem!Copy the code

3.3 The ideas and methods should be unified, and try not to be diversified.

There are many ways to solve a problem, but once you decide on one solution, don’t try another solution in another place. So when you’re building an architecture, keep in mind what you decided to do with this type of problem, and what your intentions were, and don’t waver.

In addition, you set up this module must have some ideas and reasons, to record your solution, do not change the place when you have another idea, introduce other solutions, resulting in heterogeneity.

If a framework has multiple methods or classes to solve the same kind of problem, I think the architect of the architecture must have started it without thinking it through.

3.4 No horizontal dependence, no cross-layer access if necessary.

It’s important that you don’t have horizontal dependencies, and that determines how much it will cost you to fix the architecture in the future. There is no horizontal dependency, which is a test of the architect’s module classification ability and business familiarity.

Cross-layer access is when data flows to a module with which it has no docking relationship. Sometimes cross-layer access is unavoidable. For example, the signal in the bottom layer of the network changes from 2G to 3G to 4G. This may require cross-layer notification to the View. But this kind of situation is not much, once appear to want to do everything possible in this layer fix or to the upper or lower layer fix, try not to cross the layer of the situation. Cross-layer access also increases the degree of coupling, which can be significant when a layer needs to be replaced as a whole.

3.5 There shall be restrictions on the places where the business party shall be restricted, and conditions for the flexible implementation shall be created for the business party to be flexible in such places.

Getting this right depends heavily on the experience of the architect. The architect must be able to distinguish between situations where flexibility needs to be limited and situations where flexibility needs to be created. For example, with the Core Data stack, ManagedObject can theoretically appear anywhere, which means you can modify ManagedObject anywhere, This causes managedobObject Context to synchronize changes from various sources when synchronizing changes. In this case, you need to limit the flexibility and expose only one modification interface without exposing any managed objects.

When designing an ABTest-related API, we want to increase its flexibility. The business side can implement ABtest not only in target-action mode, but also in Block mode, so as to meet the flexibility as possible and reduce the cost of the business side.

3.6 Easy to test and expand.

It’s a cliche to say that to be easy to test and easy to expand, you need to be modular and have as few dependencies as possible to mock. In addition, if you have a highly modular architecture, scaling is very easy.

3.7 Maintain a certain amount of advance.

This is an indicator of whether the architect is paying attention to industry dynamics and has an accurate grasp of technology trends. Staying technologically ahead of the curve makes it relatively easy to update your architecture.

In addition, the pre-eminence here is not only in technology, but also in products. The architect doesn’t need to deal with the product manager. Talk to the product manager and listen to his vision of where the product is going, so you can leave a path open for his vision where it makes sense. At the same time, in the startup environment, many of the product requirements are really just compromises to meet the product schedule, and will eventually turn out the right way. The business side can switch to a formal solution without implementing it, but the architecture side must be prepared for this predictable change.

3.8 Few Interfaces and few interface parameters.

The fewer interfaces and fewer parameters, the lower the cost to the business side. Of course, there are sufficient and necessary conditions to be met, and how to minimize the number of interfaces and parameters while satisfying the necessary and sufficient conditions is a demonstration of the depth of the architect’s skill.

3.9 High Performance.

Why is high performance last?

High performance is important, but it is not the first consideration in client architecture. The reasons are as follows:

  • Client services change rapidly. The primary consideration of architecture should be to facilitate the business side to quickly meet product requirements. Therefore, it is necessary to provide easy-to-use interfaces to the business side rather than provide high-performance interfaces to the business side.

  • The performance of Apple’s platforms is excellent, and there are very few user experience issues due to poor performance under normal circumstances.

  • Apple’s platform optimizations are relatively limited, and even at the expense of stability, performance improvements can be as small as 100ms to 90ms. A 10% increase in performance is not bad for servers, which tend to have hundreds of thousands of visits. Tens of thousands of 10ms is a lot. But for the user of the client side, he can’t perceive the difference of 10ms. If the user optimizes from 10s to 9s, the user can still perceive the difference, but the change from 100ms to 90ms, I think, don’t bother.

4. How to stratify the structure reasonably

4.1 In fact, layering does not have any technical content, it all depends on the experience and quality of the architect.

The common layered architecture has three layers: presentation layer, business layer and data layer. There are also four layers: presentation layer, business layer, network layer, local data layer. Layer 3 and layer 4 are not the same as layer 5 and layer 7 in TCP/IP. To be more specific, there are several logical layers of your architecture, and there are no specific specifications for what each layer is called and what it does. This is mainly for module classification.

There are also MVC architecture, MVVM architecture, this kind of hierarchy, mainly for the direction of data flow. In practice, the design for the direction of data flow and the design for the classification of modules will be put together, that is, an MVC architecture can have four layers: presentation layer, business layer, network layer, and local data layer.

In fact, there is no concept of Controller in the three-tier architecture, and the description of the three-tier architecture focuses on the logical relationship between modules. MVC has the concept of Controller, which describes the direction of data flow.

4.2 Why is three-tier architecture popular, rather than four-tier or five-tier architecture?

Because there are only three modular roles: data manager, data processor, and data presenter, that means, broadly speaking, there are only three layers of software, each with a role to play. The other fourth and fifth layers are generally derived from one of the three layers and can be concluded into a certain layer of the three layers, so it is common to use the three-layer architecture to describe them.

4.3 So how do we do layering?

How you should do layering is not something you start thinking about when you do architecture. While we design the architecture in a top-down fashion, it is generally not a good idea to start with three layers directly. Generally, it is to identify all the problems to be solved first, identify the modules, and then refine the design based on those modules. Then categorize the listed problems and modules. It’s not surprising that most of them have three levels. If a particular layer is particularly large, it can be broken down into four layers, and then five.

4.3.1 You want to design an INSTANT messaging server architecture, how to layer?

Remember, don’t start with a three-tier architecture. You won’t get a good architecture by doing that. Decide what problems you need to solve first. This is just an example, and I’ll just list some random meanings:

  1. To solve the problem of user login and logout
  2. Solve the problem of data communication between different users
  3. Solve user data storage problems
  4. If you have a cluster of multiple servers, address the user connection
  • Solve the first problem: you need a link management module, which is typically implemented through link pools.
  • Solve the second problem: there needs to be A data switch module, the data received from A to B, this module does the job.
  • Solve the third problem: you need a database, and if you’re serving a large number of users, you need a buffer that only writes when a certain amount of data needs to be stored.
  • To solve the fourth problem: there are several solutions, one is to have a cluster of several servers as pathfinding servers, all the pathfinding services to those servers, then you need to develop a pathfinding Daemon. Or use broadcast pathfinding, but if the pathfinding frequency is very high, the cluster internal network will be overloaded. That’s where you have to weigh, and the current thinking is to gocentralizedTo solve the problem of network load, you can consider configuring a cache.

So we have these modules:

Link management, data exchange, database and its supporting modules, pathfinding modules

This is far from the end, you will continue to subdivide these four modules until they are small enough. But this is just an example, so I won’t go into it.

Also, I should remind you that up until this point, it has nothing to do with layers of architecture. Once you’ve identified all the modules, start organizing your modules, which is likely to look something like this:

Link management sending/receiving data Sending/receiving data Data exchange/link management data exchange pathfinding service ========/ / ========/ database service Pathfinding service Database service /Copy the code

And then when you look at the picture after the modules are divided, well, one, two, three, three layers, so that’s a three layer architecture. This is where the most mental and challenging part of the architect’s skill lies: find all the modules you need and put them where they should be. This example focuses on layering. Performance optimization, data interaction specifications and package protocols, data collection, and a host of other necessary things are not included, but you can see how architects treat layering.

Right, the answer is no stratification. Layering comes after the architecture diagram. So if you look at any other architect’s presentation, the first sentence is almost always: “The architecture has the following layers…” . But the time to think about layering is definitely not the first time. In addition, the module must be designed to be independent, which is actually an art.

In addition, this is a server-side architecture, but the thinking is the same as a client-side architecture, with a different focus. I’m not using client architecture as an example because apple has done most of the work for you, and there’s nothing left to say.

5. About the Common folder?

5.1 For what I proposed earlierNo Common, no CoreSo why do I recommend that you don’t open the Common folder?

  • In general, we will have some common classes that belong to the project, such as fetching location coordinates, such as image processing. These modules can be very small, just h and M files. Being singled out as a module feels inadequate, but does not belong to any other module. So you’ll probably put them in Common, which is what most projects and most documentation I’ve seen so far like to do. At the time, there was nothing wrong with that.

  • But here’s the thing: software is alive and it grows. The small modules separated at that time are likely to gradually develop into large modules with the growth of the business. Once large modules are developed, they can be removed from Common to form a separate module. There’s nothing wrong with that in theory.

  • However, in the actual operation, it is not easy for engineers to consider the problem of horizontal dependence when expanding this small module, because these modules are all in Common at that time. It is very intuitive to rely on each other directly, and it is not in violation of the specification.

  • Note, however, that this is where Commom’s code gets messy, and that the Common folder allows for poorly managed dependencies. When the module dependencies in Common become complex, it is not as easy to move out and create a separate module as when Common was first set up.

5.1 Common is sometimes more than just a folder.

When using Cocoapods to manage the project library, Common tends to be a POD. The POD will have A/B/C/D/E function sets or small modules. If I want to open a new app or Demo, I will inevitably use the Common POD, which will often include the code that is not needed. I am a cleanliness freak of projects, and this situation makes me feel very uncomfortable. For example: Anjuke app in the early years did not collect all the new houses, second-hand houses, rental business. When you first started writing the new house app, you created a Common pod, which contains some code that is Common for the new house and also contains some code that is Common for the app. After half a year or a year, you will start the app of second-hand housing. I think most people will choose to include the Common of second-hand housing, so the Common is likely to embark on another development path. By the time the app for renting will be opened, Common will be so big that you won’t think about sorting out Common at this time. You will finish renting first, and Common will eventually become a piece of shit.

As for the above example, another thing to consider is that the three businesses are likely to have three Common, assuming that there are Common functions in the three Common, which are assigned to the three teams to take care of. If a sub-module needs to be upgraded, the sub-module in the three Common should be upgraded synchronously. It’s very inefficient. In addition, it is very likely that the three Common will eventually develop into incompatible with each other, but the code similarity is very high, which is not clear classification in terms of architecture.

In the middle of last year, Anjuke decided to merge its three businesses into one App. Ok, so if you are the architect, what do you do with the three Common? If you want the fastest results, you have to put up with the code redundancy and put up the shelves first, or you’ll end up with a Common that’s constantly being cut and cluttered. At this point, Common has reluctantly turned into a piece of shit. You may not know exactly what is in Common like this, and any one person would not dare to do a thorough cleaning.

Also, the Common itself is a very granular module. In a team as large as Ali, even if a new business is opened, it needs to be developed in the whole APP environment. Why? Because the granularity of module splitting is not enough, to open a new business, you must pull down all the code and dependencies of other businesses, and then open a new entry, your new business can be normal code writing and debugging. However, your new business actually only rely on the home page entrance, network library and other small modules, do not need to rely on so many other businesses that have nothing to do with you. Now every time I open the Tmall project, I have to wait for two or three minutes, which is very painful.

But do people really not know this reason? Given this reason, why isn’t anyone trying to sort out the less fine-grained modules? It seems to me that no one would dare to do it.

Originally we use good, means rotten rotten point, you changed you can guarantee not to make mistakes? For such a complicated thing, you will certainly be in trouble in a short time. The amount of tasks and working hours are not easy to estimate, and your leader will think that you are cheating the working hours to play with your own affairs. Even if you do, QA will need to do a full regression test again, and it will be very difficult to convince them to work with you. Spending all this money just to save a few minutes of waiting for the IDE to open when you start a project? I think if I were your leader, I would not approve you to do such things. Therefore, rather than suffer from this later, it is better not to set Common at the beginning of the architecture and save a lot of effort later on. Why is the work of an architect so important to the team? Why is the quality of an architect so important to the team? I think this is a good example of that.

In short, Common is not recommended for the following reasons:

  • Common is not just a folder, it will also be a Pod. Whatever it is, it’s easy to create a thick of small module dependencies in Common that allow engineers to ignore dependency management as the modules grow and make it very difficult to break them down in the future.
  • Common itself runs counter to the idea of fine-grained module design and is an inappropriate way to be lazy, and business extension will get in the way in the future.
  • Once Common is set, it opens a crack in the door of hell, and every business iteration puts something in Common that is not well classified, which creates a lot of work for the people who maintain Common, and it’s all manual work that is very error prone.

5.2 So, what are the benefits of not having Common?

  1. It forces engineers to take the dependency management into account when expanding the business, so that modules have their own soil at the beginning of development, with great space for growth and flexibility.
  2. Reduce the size of each service module or DemoCommonIs contained in the existence of.
  3. Maintainability is greatly improved, module upgrade to do synchronization work is very easy, the liberation of the helpless painCommonMaintainers, more time can be spent on more substantive development work.
  4. Conforms to the architecture idea of fine – grained module division.

Common has only one benefit: it saves a lot of work up front. However, it does more harm than good. If Common is not set, the smallest module and the smallest code will be singled out. At most, extra lines of code will take only a few minutes to write in the Podfile. But it will take more than a few minutes to undo Common’s SINS. If there are so many benefits to not using Common, why not?

Suppose you have a class for Location in your future project, even if it’s only two files, give it a module called Location. If you have a class in your project that does ImageProcess, create a module called ImageProcess. Don’t put them all in Common, so when you start a new project or a new business, use Location to rely on Location, use ImageProcess to rely on ImageProcess, don’t rely on Common, so that your project can be managed easily, The guy who runs Common is having an easy time (he’s not even needed anymore, wouldn’t it be better to put his salary on you? D) to upgrade in the future.

reference

IOS App Architecture talk