Document directory
- Win technical trends in key stages
- Incorporate these ideas into the s # ARP Architecture
- Domain-Driven Architecture concatenates them
- How?
- Learn more
- About the author
Developing Web applications is painful. Whether it is to organize and test Ajax scripts or simulate the status in a stateless environment, developing Web applications requires full concentration and dedication at all stages of planning and development. Web developers also need to face many challenges in development, such as the mismatch between objects and relationships; select the most appropriate tool among the complicated options to improve development efficiency; the application of a good architecture in the project ensures code maintainability without affecting project delivery. All kinds of problems make the development situation more severe.
Everything is under development. Although the emerging technologies and skills are gradually solving these difficulties, none of them can play the role of silver bullet. However, by balancing various technologies and carefully selecting technologies and skills, you can greatly improve development efficiency and maintainability without sacrificing quality. This article focuses on the mainstream development direction of web development. By using the s # ARP architecture, this is based on ASP. A framework of. Net MVC integrates the essence of these technologies and skills to contribute value to customers.
Win technical trends in key stages
If you want to use a word to describe the software development industry, there is no doubt that this word is "change ". Compared to those long history disciplines such as civil engineering, our industry is just emerging and still in its infancy. We are growing, and the troubles of growth are the great changes that the industry has experienced, and it seems that such changes will continue for a while.
This change is common. One example is the project management methodology. Due to its painful experience due to its misunderstanding, it has experienced great ups and downs and quickly declined from a rising star to obscurity. Another example is about the rise and fall of technology. The waves of the Yangtze River are pushed forward, and new technologies always come and go, replacing old technologies. Taking the model view presenter mode in ASP. NET as an example, although this design mode introduces better testability for ASP. NET, it increases the complexity. Recently, Microsoft introduced ASP. net mvc to replace ASP. NET. It has increased the testability of ASP. NET implementation to a new level. Because you need to consider. net web development testability. The MVP mode always places the Controller logic in ASP. code behind, and ASP. net MVC discards the MVP practice. This does not mean that the MVP implicit principle is no longer valid until now. However, with the emergence of this technology, the use of appropriate separation of concerns can better simplify MVP's implementation of testability goals.
Therefore, although the software industry remains unpredictable, the specific trends and design concepts of technological development still constitute the foundation for developing and delivering high-quality and maintainable projects. Perhaps, the implementation of these ideas will change over time, but the concept itself is a solid foundation for successful software, which can continuously affect software development. Below, I will briefly review these design concepts. Their success in the key phase has been widely accepted by the development community and thus has a profound impact on future software development.
Basic Abstract Functions
Not long ago, I was overwhelmed by writing crud capabilities for a new object. This job is as laborious and thankless as it is re-painting my house. A large number of repeated code is also the frequent occurrence of errors. Write the stored procedure and ADO. net adapter, to test the Javascript verification code of the snippet, I found that every day I put a lot of effort into the implementation of these basic functional details, so that after I write the code, do not throw them all out of your mind.
The Paradigm Transformation has developed and matured over the past decade. The Implementation Details of basic functions belong to the bottom-layer work and should be handed over to specialized tools. The challenge is to find the right tool for the job, allowing the software to ignore these implementation details and to ensure that the basic functions are usable. Nhib.pdf is a model of such tools. It can process persistence between common. Net objects and relational databases. In this way, the object itself can completely ignore how to achieve persistence, and solve the mismatch between the object and the correlation. Moreover, it does not need to write any row of ADO. Net code or stored procedures. Nhib.pdf is a great tool. More importantly, it achieves a lofty goal. By providing a fixed solution, it avoids tedious and trivial basic functions. You must know that these implementations were an important part of development activities in the past.
Over time, the software industry has introduced a large number of sophisticated tools and technologies to abstract these boring infrastructure structures and then set them after development. For example, with the gradual maturity of nhib.pdf, the additional plug-ins, such as fluent Nhibernate, have the ability to automatically map and completely reduce the burden of managing data access. This phenomenon confirms the predictions mentioned in Douglas hofstandter's book Gödel, Escher, and Bach, that the adoption of reasonable abstraction is the path to the development of software development and needs to be strongly promoted.
Loose coupling
The most common cancer of legacy software systems is tight coupling. (The term "Legacy" I use here refers to the broken software that other developers impose on you, or the dilapidated system that you developed many years ago) there are many examples of tight coupling, such as two-way dependency between two objects, service-dependent objects such as data access objects, and unit tests, if the dependent service is disconnected or unavailable, there is no way to test the service behavior. Tight coupling can lead to fragile code and make it difficult to test the code. Modifying the tightly coupled code will make developers feel as if they are in the treasure of the road, drop them without fighting, and even escape. Undoubtedly, the key to a successful software is loose coupling.
Wikipedia explains loose coupling as "the elastic relationship between two or more systems ." Therefore, the advantage of loose coupling is that you can modify any side of the programming relationship without affecting the other side. For example, in my opinion, no design pattern can be isolated from interfaces, and the alias for Dependency inversion (not to be confused with dependency injection) can better illustrate the idea of loose coupling. This technique is usually used to separate the data access layer from the domain layer.
For example, in an MVC application, the Controller or application service needs to communicate with the repository object for data access to obtain the number of projects in the database. (In this example, warehousing refers to the "service") to meet this requirement, the simplest way is to allow the Controller to create an instance of a new warehouse object. In other words, the Controller creates a specific dependency pointing to the warehouse object. For example, the new keyword is used. Unfortunately, this method will lead to many poor coupling results:
- Without real databases, it is difficult to perform unit tests on the controller. A real database is required, leading to the vulnerability of the unit test. Once the data is modified by the previous test, the problem may occur. When testing the Controller logic, we should focus on verifying the behavior of the controller, rather than whether the warehousing object on which it depends can successfully communicate with the database. In addition, to test a "real service", for example, in the example just mentioned, the warehousing service that communicates with the real database will make the unit test run as slowly as the old ox-pilot; the result causes the developer to stop running the unit test, thus compromising the code quality.
- If the controller of the instantiated service is not modified, it is difficult to replace the implementation details of the service (warehousing service. Suppose you want to migrate the storage from ADO. net is changed to Support Web Service. because it contains a specific dependency with the former, if you do not make a lot of modifications to the instantiation and controller using it, you cannot easily replace it with the latter. In most cases, once a change occurs, it will lead to an elastic bullet-this is another bad taste of this problem.
- The actual number of service dependencies of the controller cannot be determined. In other words, if the Controller is calling the creation function of some service dependencies, it is difficult for developers to determine its logical boundaries or scope of responsibility. As an alternative, the Controller dependency can be passed in through its constructor, which makes it easier for developers to understand the entire scope of responsibility of the Controller.
The alternative solution is to use the service dependency as the parameter of the controller constructor. The key improvement brought by this approach is that the controller only needs to understand the interface on which the service depends, rather than the specific implementation. For further explanation, we can compare the Controller Code in the following two sections in ASP. net mvc applications.
The following controller directly creates its service dependency customerrepository, which has a specific dependency:
public class CustomerController {
public CustomerController() {}
public ActionResult Index() {
CustomerRepository customerRepository = new CustomerRepository();
return View(customerRepository.GetAll());
}
}
On the contrary, the following controller passes the service dependency as an interface parameter to its constructor. That is, it is loosely coupled with service dependencies.
public class CustomerController {
public CustomerController(ICustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
public ActionResult Index() {
return View(customerRepository.GetAll());
}
private ICustomerRepository customerRepository;
}
Compared with tightly coupled defects, this clear separation method brings many benefits:
- The domain layer knows nothing about how to create a storage object and its implementation details. It only needs to call publicly exposed interfaces. Therefore, it can easily change the implementation details of data access without modifying the controller itself (for example, switching from ADO. Net to Web Service ). This is based on the assumption that the two interfaces are the same.
- It depends on interfaces rather than specific implementations. During unit testing, it is easier to inject the test Double Object of the storage. This ensures that unit tests can run quickly, avoiding maintenance of test data in the database, so as to focus the test attention on the behavior of the controller, rather than integrating with the database.
It is not elaborated here that dependency injection must support a separate interface and other loose coupling technologies. For more details, refer to the article dependency injection for loose coupling. (Note that the "mock" object described in this article is actually a "stub" object. The term "test double" comes from Martin Fowler's mocks instead of stubs ). In addition, the article refactoring service dependencies to interface isolation details how to convert design into interface isolation design patterns.
Test-driven development
In short, test-driven development (TDD) can effectively deliver high-quality, maintainable software and simplify the design in an all-round way. As a development technology, test-driven development will never be a flash of cake. It has been gaining popularity and is the key to determining the success or failure of software development. It has promoted the maturity of our industry.
The basic idea of TDD is to start software development with questions, and to constantly ask the system during the development process. For example, if you are developing a banking system, you may need to ask the system if it is capable of successfully processing the customer's deposit business? The key is to raise a question before implementing the behavior details. The advantage is that before writing a system, it allows developers to focus on the behavior required by the system.
To follow the guiding principles of test-driven development, follow these steps:
- If the target object and required behavior already exist, write the test.
- Compile the solution. The compilation fails.
- Compile enough code to compile the program.
- The unit test fails.
- Write enough code to pass the unit test.
- Rebuild if necessary!
Although test-driven development has not changed, its applications are still developing in daily development activities. For example, the most recent development direction of test-driven development is behavior-driven development, which attempts to bridge the gap between the technical language of code writing and the domain language in business application. In other words, behavior-driven development combines TDD with the domain-driven design described later (or with other methods you like.
Domain-driven design
From the recent development trend, domain-driven design (DDD) can be called unlimited scenery. This method focuses the attention of software development on the field and domain logic, rather than the relational database model that supports technology and technical solutions. Like behavior-driven design, DDD puts forward a lot of techniques and patterns to improve the collaboration between customers and development teams and reach an agreement on the descriptive language. Ideally, the customer should be able to understand the domain layer of the DDD application, and the encoding logic can fully reflect the customer's business logic.
I tested various methods and concluded that domain-driven design is a natural evolution of early programming methods, such as model-driven development for database data, the corresponding model can be regarded as the core of the application, and the task is to operate data. (Castle activerecord and ADO. NET Entity frameworks are both very stable and excellent model-driven design tools .) On the contrary, in DDD, databases are considered as a necessary infrastructure detail that supports domains and related logic. As a matter of fact, there is no database in DDD. Although domain-driven design requires databases, the key lies in that the persistence mechanism (the mechanism for Realizing Data Storage and acquisition) should be completely unknown in the field.
In addition, the domain-driven design not only separates data persistence from the domain. Its main purpose is to allow domain objects to own domain behaviors. For example, we cannot separate the customeraccountlogic class and decide whether the customeraccount depends on the payment date. Instead, we require the customeraccount to maintain this information. In this way, domain behavior is combined with the model itself.
The above introduction is only the tip of the iceberg of using DDD for software development technology. To learn more about Domain-driven design, read the documentation Quick Start of domain-driven design, which is a concise summary of Eric Evans's classic book "Domain-driven design.
Incorporate these ideas into the s # ARP Architecture
Each project has its own unique needs, and no framework is perfect. For developing Web applications, this is a situation where opportunities and challenges coexist. However, when faced with many choices, it is difficult for developers to determine which tools and technologies are suitable for a given project and solve the common challenges in development. For example, if you are looking for. net dependency injection tool-or control reverse containers-you can select spring. net (it far does not only provide IOC functions), unity, Castle Windsor, ninject, structuremap, and more. This is just a choice for IOC! What makes things worse is that we need to plan wisely and weigh tools and technologies appropriately in the architecture, which is a very challenging task.
In. in web development, at least at present, there is still a lack of general architecture and foundation, and various technologies and skills can be best combined in program development, select the latest technology and the quality tools developed by the open-source community based on proven practices. S # ARP architecture is based on this premise. The open-source s # ARP architecture tries to use the proven practices described in this article to carefully select Tools to improve development efficiency, ensure high quality and high system availability, and ensure good maintainability.
S # the tools and technologies used by ARP architecture are as follows:
- Interface isolation mode, which removes the dependency on the domain and control logic from the data access layer in combination with the dependency injection mode;
- The warehousing model encapsulates Data Access Using Scattered classes based on the single responsibility principle;
- Model-View-controller mode, implemented through ASP. net mvc, and clear separation of concerns is introduced between the view and controller logic;
- Nhib.pdf and its extended fluent nhib.pdf do not need to develop or maintain the underlying data storage and acquired Code to ensure that the domain has no idea about the persistence mechanism;
- Using the default General Service positioner provided by Castle Windsor, loose coupling is achieved through the preferred IOC container;
- The memory database SQLite is used to run behavior-driven tests to avoid integration with persistent databases;
- The Visual Studio template and T4 toolbox generate the project infrastructure and common crud scaffolding for each domain object without tedious coding.
The technologies and tools selected indicate that, although software development does not have a wonderful silver bullet, choosing a stable development practice and combining appropriate tools can bring great value.
Domain-Driven Architecture concatenates them
In my opinion, the key idea encapsulated into the s # ARP architecture is to reverse the relationship between the domain and the data access layer. In general application architectures, especially those closely related to the architecture recommended by Microsoft, their dependencies are from the presentation layer to the top-down, dependent on the business layer, and finally dependent on the data layer. Although this is an excessive simplification of the implementation details, it is generally recommended that the data layer be used as the bottom layer and be depended on by other layers; this is precisely based on the design model.
Although the model-driven approach has its own advantages and is applicable to many solutions, it does not meet the goal of the domain-driven approach proposed in this article. Making the domain directly dependent on the data layer can lead to another drawback: introducing bidirectional dependency between the domain object and the data access code. My former professor, Dr. Lang, said that only by making mistakes can we become experts in this field. I am trying to become an expert. I have already realized the two-way dependency between the domain object and the data access code. They are just a headache. (This profound lesson enables me to become an expert .)
So, how can we break through this fog and let the design clearly present these ideas? The solution is to clearly separate various application concerns and reverse the relationship between the domain and the data access layer, that is, define the separation interface at the domain layer, so that the data access layer can be implemented. In this way, all layers of the application only depend on the interfaces of the data access (or other external services) layer to ensure that the implementation details are unknown. This will make the design loosely coupled and easier to perform unit tests, while the system will become more stable during the maintenance phase of project development.
Demonstrate the architecture proposed by the S # ARP architecture, and use the interface Isolation Mode to reverse the dependency between the traditional domain and the data access layer. Each box represents a separate physical assembly, and the arrow represents the dependency and its dependency direction.
Note: The data layer in the figure defines the specific implementation details of warehousing, which depends on the core domain layer. In the core layer, besides defining the domain model and logic, the warehousing interface is also defined. This interface can be called by other layers. For example, the application service layer communicates with the warehousing, this ensures that it is independent of its implementation details. Some suggestions hold that the presentation layer (yourproject. Web above the figure) should not be directly dependent on the domain layer. As an alternative, you can introduce a data transmission object (DTO) to better separate the domain layer from the presentation layer when data is transferred to the view presentation.
How?
The bottom line of our work is that Software Delivery experts must deliver solutions in a timely manner and meet customer needs with high quality and maintainability. Many practices have been refined. The so-called "previous generations planted trees, and future generations took advantage of them" means that in personal applications, we do not have to create new ones. Instead, we only need to resort to verified and realistic design models, the implementation of basic tools can solve common problems in development, such as data persistence and unit testing. The real challenge lies in the rational planning and trade-offs in advance, which not only improves development efficiency, but also prevents us from killing our capabilities, it is not advisable to try to meet unrealistic needs in an innovative way. I hope that the technology and tools described in this article, as well as the combination of S # ARP architecture, can prove that although all roads can access Rome, we should learn from the past and avoid repeating the same mistakes, we must learn from the beginning and have enough wisdom to win a stable start.
Learn more
S # The ARP architecture project has been developed for nearly a year. It provides a simple but powerful architecture foundation for rapid development of stable domain drivers. You can download the rcflood of the s#arp from http://code.google.com/p/sharp-ubunture. The GA version of 1.0 is closely related to ASP. net mvc 1.0. S # The ARP architecture Forum welcomes everyone to speak and talk about their experiences.
About the author
Http://devlicio.us/
If you want to write beautiful software, Billy McCafferty can be said to be experienced. After a long time on the battlefield, he is born and blindly pursuing programming romance, so that he is hopeless. Billy is now in two roles. He manages codai, a small-scale training and consulting company (a new website will be launched soon), and leads developers and architect teams at Parsons brinckerhoff. After the release of S # ARP architecture 1.0, Billy's life will go back to the regular stage. You will be able to see him at alt. NET and other development conferences in the near future.