Title: GAP Component Architecture Status: Current Created: 2006-03-28 The component architecture is based upon a similar architecture in Zope3. In uses several modules from zope3 in fact as it's foundation. In GAP, applications consist of components known as services and utilities. Services As in zope3, services are the mission critical components of an application. If a service will not load then the application is non-functional. An example of a service would be the datasource manager service in an application that processes information in the database. Utilities Utilities are less critical components of an application. A failure to load a utility doesn't result in application failure. An example of an utility would be a component that adds a google style search bar to your applications toolbar. It's worth noting that almost any component might be a service in one application and a utility in another. For example, the datasource manager component would be application critical in an application that manipulated an database. However it might be a utility in a call center monitoring application that attempted to store logs in a database but could continue to display information if the database connection was unavailable. More on Components Component Interfaces Components are written to an interface and can only communicate with each other across that interface. Once a component interface API is stablized that interface will remain consistant. While slanted toward java application the "Life-cycle of an API" and "Preservation of Investments" sections of the document at http://openide.netbeans.org/tutorial/api-design.html are worth taking into consideration Component Assembly Components are basically the building blocks of an application. An application developer will take existing GAP components, add a few custom components and plug them together to form a new application. This assembly can either be done in python code for simple applications or it can be done via an application specific manifest file. By providing for a file based assembly of components we make it simple to take an existing application such as GNUe Designer, remove components we don't need, and tack on new components to the remaining pieces. Component Registry The component registry is responsible for maintaining the current components loaded into an application. It handles the loading of components, processes requests for handles to components, reloads components if required. It will also probably deal with dependency mapping so that components get loaded in the proper order. Since all components will interface with the component registry, it's interface will be kept small and simple. Components are registered by both the Interface they provide and by a name. Advantages of the Component Architecture (some taken from IRC logs) Extensiblity New components can be added to applications at the end user site. Say I wanted to write a call queue monitor for my call center at work to replace the full screen one we currently use. I want this monitor to add a button to my GNUe Forms toolbar that shows current number of calls holding. If the button is pressed I want it to launch my full screen monitor. By creating a component that registers this button on my Forms toolbar I've extended forms without needing to know all the Forms internals. I only needed to learn how to load and register my component with GAP and the API to add a button to the toolbar. Later if I were to port my primary sales application to the GAP platform then I could reuse that same component in that app as well. Maintainability One issue we've faced in the GNUe applications is broken encapsulation. Often times we hard link through numerous classes to get to the instance we need. An example from GNUe Forms reads self._dataSourceLink._connection.rollback () With components we'd do something along the lines of queryComponent(IConnectionManager) or queryComponentPool(IConnectionManager) to get a handle a connection manager that was registered for the application. As a result code no longer needs to care about the location of an instance in the application heirarchy. It only needs to know the Interface it requires and the API that interface provides. Component APIs will also be kept as clean and small as possible in order to maximize flexibility in the future. The less API we expose the easier it is to replace that component with a new implementation and not break existing code. Flexibility Component based applications also provide unique oprotunities in customization of applications. It is possible to replace or extend existing services by replacing that service in an application. While this would be handy in testing new replacement components another possibility exists. Since components ask for other components by Interface it is possible to wrapper existing components inside another component that modifies the behaviour of the existing component. For the pattern crazed amongst us I believe you'd call this a decorator pattern. An example Example: Connection tracking Lets say FooCorp wants to log all login attempts on their system to a line printer. They could ask us to add the functionality to out connection manager. However this is unlikely to be possible for every request and would result in a fairly bloated connection manager code base and API over time. They could modify the existing connection manager login method to send the desired output to the line printer. But they'd have to then track updates to our base code and remerge their changes after each upgrade. They could subclass our login manager, add a call to the login method to send the desired output to the line printer, then call the existing method in the superclass. They would then need tofind all references to the original class and replace them. Again creating update maintenance after each new release. But with the component system they could still subclass our connection manager, or write their own to the same API. They would then adjust the application manifest to load their connection manager instead of our own. When loaded, their custom connetion manager still would register itself as proving the IConnectionManager interface. GAP appliations are not coded to a specific connection manager class located in a specific module. Instead they query the component registry for the component that provides an IConnectinManager interface, like this. queryComponent(IConnectionManager) So existing applicatoins will not notice the change. If they've subclassed our connection manager then they would get any security updates without requiring any appication changes. It would even be possible to provide a system wide override file for these components that would allow their on site replacements of components to stay in effect between upgrades to applications manifest files.