Saturday, May 29, 2010

Z++ and Software Architecture

Introduction

The scope of this note is the architecture of large (enterprise) distributed applications. Without proper familiarity with prerequisites the contents may sound philosophical or impractically theoretical. A reasonable degree of maturity in architecting large applications is expected.

We recapitulate a few points discussed in the references listed below for language prerequisite.

A component consists of a set of classes or tasks (Z++ threaded form of class), and global functions that glue together instances of such classes. A component captures a specific aspect of an application, which cannot be cast into a plain set of classes. In particular, a component is a complete program that can be executed and used on its own merits.

Applications are composed from components. The new application can adjust the use of its components by specifying its own invariants and constraints.

When composing a new application, the geographic location of execution of its components within the Internet is irrelevant in that it does not require code modification.

A number of examples accompanying Z++ Visual demonstrate that components used in the composition of an application can be derived from one another the same way as classes are derived.
A Z++ autonomous agent is a component that travels from node to node maintaining its state as a process. This is known as strong mobility.

References.

1. Component illustrates the use of components.
2. Invariant discusses invariants and constraints.
3. Threads briefly reviews global threads and tasks.

In addition, Z++ Language Reference provides an overview of the language and illustrates significant features like Autonomous Agents, signaling mechanism, etc.


Architecture and Design

Definition. An application comprises of a set of (distributed) communicating components.

Since an entire application can serve as a component in composing a larger application this definition is recursive in nature. Specifically, in Z++ the distinction between an application and a component is only conceptual. When a component is used as a final product for a particular solution, we think of it as an application. On the other hand, when the same application is used in composing larger applications, we think of it as a component.


The use of a live (executing on some node within the Internet) application as a component does not interfere with the operations of the application. In other words, a new application can simply use any number of live applications as components in addition to possibly other components in its architecture. We use the term module when referring to a live application used as a component. The term module is conceptual and has no corresponding linguistic mechanism in Z++.

Another conceptual term in connection with components is layer. The term layer in Z++ is related to layers of exception and is not associated with components. Nonetheless, the term layer is generally referred to a group of components that together capture a particular level in the architecture of an application. For instance a compiler is usually implemented in layers.

The notions of architecture and design are distinct. At architectural level we are concerned with components and their modes of communication. Eventually, each component must be implemented by, designing classes, tasks, global functions and calls to libraries. That is, at design level we are concerned with actual implementation of a specific component. The smallest architectural unit is a component. A class, task or a global function is a unit of design within a component.


Comprehendible Size

We need to differentiate between the size of an application and the size comprehendible by a single developer. Theoretically, there is no limit to the size of an application, especially when any number of parts of an application could be running on distinct nodes. However, a developer needs to properly understand the code he/she is dealing with in order to find defects and fix them without breaking other parts of that code.

Multiple developers for the same component, does not raise the comprehendible size noticeably. Here and there it helps in locating defects, and occasionally in avoiding the introduction of new defects. The total comprehendible size, however, grows very slowly and in fact beyond a threshold, depending on team members, it begins dropping. For the sake of having a number, I assume that the comprehendible size is about 100,000 lines of coherent C++ code. By coherent I mean the code that the engineer wrote for a particular solution, excluding the understandability of external libraries and technologies beyond a black box.

On the average, an equivalent component in Z++ would be about 75,000 lines of coherent code. In fact, the more complex the code in its use of database, distributed computing, threading and other advanced concepts, the larger the difference in lines of code between C++ and Z++. It is also important to note that Z++ abstractions are easier to understand which in turn reduces the possibility of defects. A lot of defects get buried in lower level details used for the implementations of partial equivalents to Z++ abstractions.


Types of Components

In previous section we mentioned two special forms of use of components, namely a module and a layer. In this section we name a few more.

It is not possible to create a complex application without putting together a set of complex components. Note that by a complex component I mean the natural complexity of the problem being solved, not that the component is written badly.

Large enterprise distributed applications cannot be directly broken down to classes, nor a component is the equivalent of a class. A component is the conceptual understanding of a major aspect of an application for purposes of implementation in a programming language. Although at architectural level we are not concerned about implementation, we should be aware of the capabilities of the selected language. Those capabilities affect the architectural decisions. After all, the architect of an edifice must be aware of the availability of techniques and material for erecting his/her drawing.

In composing an application we can use different forms of modules (live components). Sometimes we directly use a module as part of the new application, and other times we may only need to interact with another application in the form of a server. In the latter case, a component for the new application can interact with a database or an Internet server rather than the server becoming a component of the new application. We refer to the component that interacts with a server as a wrapper. Basically, the new application makes calls into its wrapper component, which in turn communicates with the server via a protocol.

Sometimes an application needs to interact with a set of applications (including servers). Furthermore, the overhead of communicating with each of these applications is too great for the amount of final data that is needed in order to make a decision. In such cases, the application can send out a component that knows where to go and collect the required data, and finally return to the original application. This type of traveling component can be implemented as a Z++ Autonomous Agent. Upon its return the agent can signal its arrival and provide the requested data to the application.

An autonomous agent can do its job in other ways. The agent may go to a node, and after gaining the needed data it can transmit it to its mother application. The agent can then move to other nodes and do the same thing until its operation is completed. For instance, it may receive instructions to return to a previous node and perform some further operations (finalize a reservation, shut down a service, etc).

It should be clear that a Z++ autonomous agent can interact with servers written in any language. For instance, database servers are written in C but they communicate via SQL.


Conclusion

The main point we are making is that, when a program fits within a single component our activity begins with design. We think of classes, functions, threads, signals and libraries for the implementation of the program.

On the other hand, we are saying that today’s applications are generally large and distributed. The development of applications begins with architectural activities for laying out the characteristics of components and their interactions in a distributed setting.

The language Z++ provides a uniform view of abstractions for building large distributed application, and the distributed operating system Z47 is a cushion for longevity of Z++ applications. Physical computing devices are merely a means for user interaction with applications. An application is an abstraction independent of the set of physical devices on which it runs. Thus, an application must outlast short-lived physical computing devices.

Presently, there are system applications that are designed for particular devices. Some of these system applications, such as games, may remain so for a long time if not forever. However, enterprise solutions packaged by the vendor of a device are not good examples of system applications. Unlike games, enterprise solutions use a variety of platforms, each of which may be presented on a set of computing devices.

Labels: , , ,