Skip to content
This repository has been archived by the owner on Sep 3, 2021. It is now read-only.

Commit

Permalink
Updated documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
rraziel committed Apr 13, 2019
1 parent 566219d commit f4ca612
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 53 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

A modern, lightweight implementation of dependency injection inspired by [JSR-330](https://jcp.org/en/jsr/detail?id=330) and [Spring](https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html).

[![Version](https://img.shields.io/npm/v/es-injection.svg?label=Version&style=for-the-badge&logo=npm)](https://www.npmjs.com/package/es-injection)
[![Downloads](https://img.shields.io/npm/dt/es-injection.svg?label=Downloads&style=for-the-badge&logo=npm)](https://www.npmjs.com/package/es-injection)
[![Version](https://img.shields.io/npm/v/@es-injection/core.svg?label=Version&style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@es-injection/core)
[![Downloads](https://img.shields.io/npm/dt/@es-injection/core.svg?label=Downloads&style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@es-injection/core)
[![AppVeyor](https://img.shields.io/appveyor/ci/rraziel/es-injection/master.svg?label=Win32&style=for-the-badge&logo=appveyor)](https://ci.appveyor.com/project/rraziel/es-injection)
[![CircleCI](https://img.shields.io/circleci/project/github/rraziel/es-injection/master.svg?label=MacOS&style=for-the-badge&logo=circleci)](https://circleci.com/gh/rraziel/es-injection)
[![Travis CI](https://img.shields.io/travis/rraziel/es-injection/master.svg?label=Linux&style=for-the-badge&logo=travis)](https://travis-ci.org/rraziel/es-injection)
Expand Down Expand Up @@ -37,25 +37,25 @@ This loose coupling tends to make code more self-contained and easier to test, a
The library can be installed using `npm`:

```
npm install es-injection --save
npm install @es-injection/core --save
```

Or using `yarn`:

```
yarn add es-injection
yarn add @es-injection/core
```

## Development

The module can be built using the following command:
The modules can be built using the following command:

```
npm run compile
yarn run build
```

It is also possible to keep unit and integration tests executing as a background task:
Tests are written using [Jest](https://jestjs.io/) and can be kept running as a background task with:

```
npm run test:watch
yarn run test:watch
```
6 changes: 4 additions & 2 deletions doc/annotation-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ This effectively lets the application context create `MyComponent` instances.

### Component Instance

Sometimes a component cannot be easily instantiated, either because it requires specific settings that are not available to the component and need to be passed in, or simply because the objects are from third-party libraries.
Sometimes a component cannot be easily instantiated, either because it requires specific settings that are not
available to the component and need to be passed in, or simply because the objects are from third-party libraries.

In this scenario, components can be created directly within the configuration class:

Expand Down Expand Up @@ -105,7 +106,8 @@ As projects grow larger, having multiple configuration classes - one per sub-mod

In many cases, components defined within a sub-module will have dependencies that come from other sub-modules.

For such a scenario, requiring the user of sub-module A to know they need to add the configuration for sub-module B simply because A requires B is not convenient.
For such a scenario, requiring the user of sub-module A to know they need to add the configuration for sub-module B
simply because A requires B is not convenient.

To work around this, the @Import decorator can be used:

Expand Down
8 changes: 5 additions & 3 deletions doc/application-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ The application context is the central interface for [component injection](compo

## Context Lifecycle

The application context has a lifecycle that determines what type of operation can be performed with the application context.
The application context has a lifecycle that determines what type of operation can be performed with the application
context.

The lifecycle can be describe as the following steps:

Expand All @@ -21,7 +22,7 @@ The lifecycle can be describe as the following steps:
3. the execution phase
4. the [context stop](#context-stop)

The context gets configured and, once started, becomes read-only.It is configured once and then, once started, becomes read-only.
The context gets configured and, once started, becomes read-only.

## Context Configuration

Expand All @@ -38,7 +39,8 @@ Annotation-based configuration makes it possible to:
- declare other configurations that are dependencies, via the `@Import` decorator
- provide component instances via methods annotated with a `@Component` decorator

More information regarding annotation-based configuration can be found in the [Annotation-based Configuration chapter](annotation-configuration.md).
More information regarding annotation-based configuration can be found in the
[Annotation-based Configuration chapter](annotation-configuration.md).

## Context Start

Expand Down
18 changes: 12 additions & 6 deletions doc/component-declaration.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@

## Introduction

Not documented yet.
Declaring a component allows its lifecycle to be managed by es-injection.

The decorators necessary for component declaration can be imported from the @es-injection/decorators module.

## Basic Declaration

Declaring a component is as simple as adding a `@Component` decorator to a class.

```typescript
import {Component} from 'es-injection';
import {Component} from '@es-injection/decorators';

@Component
class MyDependency {
Expand All @@ -34,7 +36,8 @@ As an alternative, it is also possible to use one of three more specific stereot
- `@Repository`, dedicated to the persistence layer
- `@Service`, dedicated to the service layer

All four decorators exhibit the same behavior, but the annotated code is usually more readable when using the proper stereotype.
All four decorators exhibit the same behavior, but the annotated code is usually more readable when using the proper
stereotype.

It also makes it possible to filter components based on their stereotype.

Expand All @@ -43,23 +46,26 @@ It also makes it possible to filter components based on their stereotype.
The aforementioned decorators also make it possible to give a name to a component.

```typescript
import {Component} from 'es-injection';
import {Component} from '@es-injection/decorators';

@Component('my-dependency-name')
class MyDependency {

}
```

Components with a name can be queried by name, either programmatically through the [application context](application-context.md) or using the [@Named](component-injection.md#named-dependencies) decorator.
Components with a name can be queried by name, either programmatically through the
[application context](application-context.md) or using the [@Named](component-injection.md#named-dependencies)
decorator.

## Abstract Class vs. Interface

Currently, interfaces are not concrete types and do not get compiled into an actual object.

Due to this, it is not possible to add decorators to an interface and then retrieve components through this interface.

It, however, possible to achieve something quite similar by creating an abstract class with nothing but abstract methods.
It, however, possible to achieve something quite similar by creating an abstract class with nothing but abstract
methods.

The following interface:

Expand Down
136 changes: 123 additions & 13 deletions doc/component-injection.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,145 @@
# Injecting Components

TODO
- [Introduction](#introduction)
- [Dependencies](#dependencies)
- [Constructor-based Injection](#constructor-based-injection)
- [Method-based Injection](#method-based-injection)
- [Property-based Injection](#property-based-injection)
- [Named Dependencies](#named-dependencies)
- [Optional Dependencies](#optional-dependencies)
- [Containers](#container-dependencies)
- [Arrays](#arrays)
- [Maps](#maps)

## Introduction

The main advantage of using es-injection is that components may be injected into other components.

This helps properly separating the responsibilities of each components, while making testing more straightforward as
injected dependencies can simply be mocked.

## Dependencies

TODO.
Injecting a dependency is done using the `@Inject` decorator.

### Constructor-based
A number of additional decorators also make it possible to further refine what gets injected.

TODO.
### Constructor-based Injection

### Method-based
Since es-injection manages the instantiation of components, it needs to be able to provide all constructor arguments.

TODO.
In practice, it means all constructor parameters come from injection without requiring the `@Inject` decorator.

### Property-based
```typescript
@Component
class MyComponent {
constructor(dependency1: DependencyComponent, dependency2: OtherDependencyComponent) {
}
}
```

TODO.
While convenient, constructor-based injection should be limited to dependencies that are necessary to the instance
construction.

When that is not the case, [method-based injection](#method-based) should be favored.

### Method-based Injection

Method-based injection tells es-injection to inject a dependency via a method call.

For such cases, the `@Inject` decorator is required.

```typescript
@Component
class MyComponent {
@Inject
setDependencies(dependency1: DependencyComponent, dependency2: OtherDependencyComponent): void {
}

@Inject
setMoreDependencies(dependency3: YetAnotherDependencyComponent): void {
}
}
```

Multiple methods can be decorated with `@Inject`, but common practice involves injecting a single dependency per method
call though.

### Property-based Injection

Property-based injection tells es-injection to inject a dependency directly into a property.

Doing this required the `@Inject` decorator as well.

```typescript
@Component
class MyComponent {
@Inject
dependency1: DependencyComponent;
@Inject
dependency2: OtherDependencyComponent;
}
```

While convenient, property-based injection can prove difficult to work with as such injected properties are generally
meant to be private and could therefore not be set from outside the class, e.g. for tests.

## Named Dependencies

TODO.

## Optional Dependencies

TODO.
An optional dependency is a dependency that may not be available within the
[application context](./application-context.md).

## List Dependencies
When encountering such an optional dependency, the injected value will be `undefined` if the dependency is unavailable.

TODO.
```typescript
@Component
class MyComponent {
@Inject
setDependency(@Optional dependency: DependencyComponent|undefined): void {
}
}
```

## Map Dependencies
## Containers

TODO.
Sometimes multiple classes match an injection request, such as injecting a based class that has multiple derived
components.

In that case, an array or a map can be used to inject all available components.

Due to current limitations with the code emitted from a TypeScript compilation, es-injection cannot know the type
to inject when using arrays or maps.

To work around this issue, the `@ElementClass` decorator must be used.

### Arrays

When injecting an array, the `@ElementClass` decorator hints at the type of the elements in the list.

```typescript
@Component
class MyComponent {
@Inject
setDependencies(@ElementClass(BaseClass) dependencies: Array<BaseClass>): void {
}
}
```

### Maps

When injecting a map, the `@ElementClass` decorator hints at the type of the values in the list.

The keys must always be strings, and will be each component's name.

```typescript
@Component
class MyComponent {
@Inject
setDependencies(@ElementClass(BaseClass) dependencies: Map<string, BaseClass>): void {
}
}
```
15 changes: 8 additions & 7 deletions doc/component-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

A component possesses a lifecycle, with two phases that occurs during the component's construction and destruction.

It is possible to execute logic during those phases, which are generally used to initialize a component or shut it down, respectively.
It is possible to execute logic during those phases, which are generally used to initialize a component or shut it
down, respectively.

## Construction Phase

Expand All @@ -30,7 +31,7 @@ The construction phase starts when a component needs to be instantiated. This ph
A post-destruction method is a method decorated with the `@PostConstruct` decorator:

```typescript
import {Component, PostConstruct} from 'es-injection';
import {Component, PostConstruct} from '@es-injection/decorators';

@Component
class MyComponent {
Expand All @@ -48,7 +49,7 @@ class MyComponent {
It is also possible to perform an asynchronous cleanup, simply by having the method return a promise:

```typescript
import {Component, PreDestroy} from 'es-injection';
import {Component, PreDestroy} from '@es-injection/decorators';

@Component
class MyComponent {
Expand All @@ -66,7 +67,7 @@ class MyComponent {
The pre-construction method call order can be explicitely set using `@Order` decorators:

```typescript
import {Component, Order, PostConstruct} from 'es-injection';
import {Component, Order, PostConstruct} from '@es-injection/decorators';

@Component
class MyComponent {
Expand Down Expand Up @@ -106,7 +107,7 @@ The destruction phase is simpler, but still made of multiple steps:
A pre-destruction method is a method decorated with the `@PreDestroy` decorator:

```typescript
import {Component, PreDestroy} from 'es-injection';
import {Component, PreDestroy} from '@es-injection/decorators';

@Component
class MyComponent {
Expand All @@ -124,7 +125,7 @@ class MyComponent {
It is also possible to perform an asynchronous cleanup, simply by having the method return a promise:

```typescript
import {Component, PreDestroy} from 'es-injection';
import {Component, PreDestroy} from '@es-injection/decorators';

@Component
class MyComponent {
Expand All @@ -142,7 +143,7 @@ class MyComponent {
The pre-destruction method call order can be explicitely set using `@Order` decorators:

```typescript
import {Component, Order, PreDestroy} from 'es-injection';
import {Component, Order, PreDestroy} from '@es-injection/decorators';

@Component
class MyComponent {
Expand Down
Loading

0 comments on commit f4ca612

Please sign in to comment.