Hi,

To be honest i am glad there a feeling of curiosity and interest about 
components.
I have seen the light my friends and I am a believer :-)
And there is hope for a "born again 4D component lovers" community.

I find it sad when I hear 4D developers saying that they tried components and 
that they gave up.

Maybe I can share my experience with components.

I have been using components since day 1 (early adopter) and I am *very* happy 
with them.
It really changed the way we write code.

I did a summit presentation in back 2012 Paris about "4D development 
industrialization with components".

And I attended Peter's very good presentation on components last year in 
european summit. Peter is also a fan of components.
Interestingly enough we use it in a difference ways (i'll come back to that), 
but we have encountered the same problems/pitfalls and found some 
solutions/workround. 

A bit of context first...

We are a bespoke developer company, mainly 4D, a bit of Oracle and php/web, 
etc.. (several dev, many projets, across several 4D versions).
We work in remotely (not coding together on the same 4D Server).

Before components we would have a common source library (a shell if you want).
We would move code with insider. But Insider is gone and maybe it was a good 
thing.

Early on, the methods of our library were grouped in modules (which helped us a 
lot to move stuff into components).

I looked into components as an alternative to 4D Insider.

I an idea of what I did not like :
        - bloated components
        - rely on too many human operations (boring, errors)

And what I wanted :
        - reduce duplicated code across projets
        - re-use technical code
        - improve code quality
        - fixe code once and share fixes across projects
        - versionning
        - avoid naming collision (a sort of namespace)
        - performance (using compiled components in interpreted host is great 
compromise)
        - logs to diagnose stuff
        - documentation
        - unit tested code (in an ideal world)
        - move more than just code/methods, sometimes resources, etc...
        - share code between versions (I don't enjoy moving code and fixes 
across libraries in several 4D versions)
        - wanted to simplify updating code when changing 4D version
        - modular approach : have access to a collection of component where we 
could choose and pick according to the needs of a given project.

I came to the conclusion, early on, that our code was made into two parts :
        - business code
        - technical code

The business code is rarely re-used. Also, working on tables, etc... is not 
simple with components (working with SQL or pointers) make things more 
complicated and brittle in my opinion. 
And I have never used component "local" data.
So I don't do business code in components. I know Peter has a different 
approach on this. It is fine, there is no right or wrong. Juste different 
taste, experience, needs and methods.


The technical code is in fact a collection of modules which can be, for the 
most part, independant of each other.
So for instance we have written about 40 to 50 components.

Some projects are using 2 or 3 components. Some are using 30+.

Some are published with source code for free (aws_component for S3 for 
instance).

Some are commercial (ociLib to help coding with 4D for OCI and provide 
retro-comptibilty to the legacy "4D for Oracle" plugin).

The vast majority is private and used internally. 
This is for instance, a part of our collection :        
        - BLB : blob stuff
        - CRC : checksum (md5, sha1, sha2, sha384, sha512, crypto)
        - CPDF : pdf handling stuff (using "Coherent pdf" binaries)
        - DBG : log writing
        - ENV : information about system (os, disks, etc...)
        - FS : filesystem stuff
        - HEX : hexadecimal stuff
        - HTTP : http stuff
        - HTML : html stuff
        - IC : internet commands stuff
        - IO : input / output (import and export, cdv, etc...)
        - PDF : pdf printing stuff
        - PRM : handling "environment" parameters
        - PTR : pointer stuff
        - MSG : alerts, messages and progress stuff
        - OBJ : object stuff (well this one is Cannon's object module converted 
into a component)
        - SEM : semaphore stuff
        - SMTP : smtp stuff
        - TAB : array stuff
        - TXT : text
        - XDOC : auto documentation
        - XML : xml stuff
        - ZIP : 7zip stuff (compression and AES encryption)
        - etc.

You get the idea...

Most of theses components are independent to each other.
Few higher level components have "strong coupling" because they explicitly call 
another component. When you use a compiled application, 4D will detect the 
missing component an complain when you open you structure.
Most have a "soft coupling" : most components spit out logs. For this, they 
call the "DBG_component". But this is done with a call to EXECUTE METHOD (only 
if the DBG component is installed). So that the component will work with or 
without the "DBG_component". In effect, the "DBG_component" is optional.

But it is also ok to duplicate code in components. Let's have a look in the C 
world. Do you see libraries always linking to the md5 system library ? No. md5 
is a simple C algorithm therefore is is often duplicated and included and 
statically compiled with the source.
So if you need a simple function from another component, don't be dogmatic and 
avoid dependency hell. I prefer duplication code from one component to another 
than introducing a dependency.

What I discovered also, is that components are a great way to embed binaries.
For instance I embed 7zip binaries (mac and windows for each architectures) in 
the zip. It is sometimes "big" it it can avoid dependencies on system, 
installing libraries, incompatible versions, etc...
Also you could embed php scripts in a component. Recently I wrote a component 
for fellow developers in Czeck republic which did xmldsig with soap (http 
client, xml and crypto signature). This was done in a component. To do this I 
need crypto function from php to sign and get public from p12 password 
protected certificate file. So I juste wrote the php function in a php file, 
embedded in the resource dir and wrote the rest of the code in 4D. A bit of 
unit test on all this. Et voila. This works great.

All our components and projects are in Unicode mode.

Also I still write most of my components in v12. And they can be most of the 
time used in more recent of 4D v13, v14.
For 4D v15 I need recompiling because of Mac 64 bits missing in earlier 
compiled components.
Of course it is difficult to benefit from new features (but it is sometimes 
possible).
But I will soon move my components to a newer version (4D v15 maybe). And I 
won't back port new features bug fix to v12 (it is a pain).

For instance, 4D introduce a nice "4D Progress" component in v13.
We our own component in earlier versions to do that "MSG_component". So I 
updated our component in v13 and made it call the "4D Progress" component.
So when converting on of projects from v12 to v13, we just replace/update the 
MSG component and *boom* we suddenly have "4D Progress" thermometers without 
rewriting a single ligne in the host database.  

By the way I check if a component is present only once at init. Then I use an 
interprocess variable to see weather or not I should call the other component 
with EXECUTE METHOD.
I haven't measured if EXECUTE METHOD has a significant overhead to be honest...

Yes there are things which are a bit tricky, like isolation host/component. It 
is not always obvious.

There is an effective (and relatively simple) workaround : use callbacks ! 

The first one I had was "get pointer" from a variable name. Once you understand 
the value of "callbacks", it becomes natural to write callbacks.
Some which I recently stumbled on : list, io filter, etc...
Yes documentation is not very explicit in terms of host/component behaviour. So 
it sometimes you'll needs trial and error/proofing/testbed.

Moving further with callback, you can introduce IoC (inversion of control). The 
"Hollywood Principle" : "Don’t Call Us, We’ll Call You".
For instance a component can read an Excel file and call the host callback a 
method in the host to see what to do with the data found in Excel...
The callback can be called for each cell and the host code can decide what to 
do with the data. 

NOTE : for callback -> don't forget to use "share method between host and 
component".

Most of these things when you think about it may feel a bit like a pain, but 
not having this isolation would be WORSE !!! 
Without this, components could really alter the behaviour of host and hurt you. 
This would be not good.

Hint/tip : callback could be added automatically by component into the host on 
init.

It is true sometimes it would make things easier if there was more commands 
which could behave in either context of host/component with the magic *.

I use a naming convention. All methods of components are prefixed with a 
component code.
For instance, for the blob component => component code BLB.
BLB_blobAppend => one underscore means visible and public (shared between host 
and component)
BLB__somePrivateMethod => two underscore means hidden and private (note shared 
between host and component)

We use lots of logs (another subject altogether) and more and more ASSERTS to 
see if the component received correct parameters (check pointer pointer types, 
nil pointer, assert errors, etc...)

Also when you make components, you have to realize that you effectively create 
a "public" API and you have to think forward and to think backward 
compatibility.
A new version of the component could break an app if you change parameters, 
etc...
So it needs to be done in a careful way (and to be documented).

Therefore we are "versioning" the components.

We decided to use compiled components. We use the compiled component even when 
we write code in the interpreted host. So there is no surprise.
Because we use compiled code, we cannot see comments. Therefore we needed good 
documentation.
We ended up writing our own documentation system (with custom tags in header) 
to generate pretty documentation which is inserted in the method "comment" 
section and is visible in host.

We also sometimes, can fix an application, by just installing the new version 
of the component if the bug is in the component. So we don't have to deploy the 
complete application (and we don't therefore we don't need to test it if the 
new component has been properly tested).

There are things I want to improve. I would like to make the process to making 
a new version of a component more automatic (faster, less error prone, not 
really interesting moving files around, etc...), maybe export source into git. 
It is definetly possible I just haven't taken the time to so it.

Conclusion :
Components are a great way to re-use utility code.
We have adopted a modular approach and we are very happy with it.

This is just my experience and my view...

HTH
Bruno
**********************************************************************
4D Internet Users Group (4D iNUG)
FAQ:  http://lists.4d.com/faqnug.html
Archive:  http://lists.4d.com/archives.html
Options: http://lists.4d.com/mailman/options/4d_tech
Unsub:  mailto:[email protected]
**********************************************************************

Reply via email to