Organizowanie wielu funkcji


Gdy integrujesz Cloud Functions z projektem, Twój kod może się rozrastać i zawierać wiele niezależnych funkcji. Możesz mieć zbyt dużo funkcji, aby zmieścić je w jednym pliku, lub różne zespoły mogą wdrażać różne grupy funkcji, co stwarza ryzyko nadpisania lub przypadkowego usunięcia funkcji należących do innego zespołu. Cloud Functions oferuje różne sposoby porządkowania kodu, aby ułatwić nawigację i zarządzanie funkcjami.

porządkowanie funkcji w bazach kodu;

Aby zarządzać dużą kolekcją funkcji w wielu repozytoriach lub podpakietach w ramach konfiguracji monorepo w pojedynczym repozytorium, możesz użyć właściwości codebase obiektu konfiguracji funkcji w firebase.json:

# firebase.json
"functions": {
  "codebase": "my-codebase"
  # NOTE: Codebase must be less than 63 characters and can contain only
  # lowercase letters, numeric characters, underscores, and dashes.
}

Właściwość codebase jest obsługiwana w wersji Firebase CLI 10.7.1 i nowszych.

Zarządzanie wieloma repozytoriami

Właściwość codebase ułatwia zarządzanie wieloma repozytoriami. Przyjrzyjmy się sytuacji, w której masz 2 różne repozytoria, które wdrażają funkcje w tym samym projekcie Firebase:

$  tree .
├── repoA
│   ├── firebase.json
│   └── functions
│       ├── index.js
│       └── package.json
└── repoB
    ├── firebase.json
    └── functions
        ├── index.js
        └── package.json

Bez adnotacji kodu źródłowego wiersz poleceń Firebase wyświetliłby podczas wdrażania prośbę o usunięcie funkcji zdefiniowanych w innym repozytorium:

$ (cd repoA && firebase deploy --only functions)
...
i  functions: preparing functions directory for uploading...
  functions: functions folder uploaded successfully
The following functions are found in your project but do not exist in your local source code:
        fn1FromRepoB
        fn2FromRepoB
        ...
? Would you like to proceed with deletion? Selecting no will continue the rest of the deployments. (y/N)

Aby uniknąć tego problemu, dodaj unikalną adnotację kodu źródłowego w sekcji konfiguracji funkcji w pliku firebase.json w każdym repozytorium projektu:

# repoA/firebase.json
"functions": {
  "codebase": "repo-a"
}

# repoB/firebase.json
"functions": {
  "codebase": "repo-b"
}

Dzięki adnotacji kodu źródłowego wiersz poleceń Firebase nie będzie już wyświetlać prompta o usunięcie funkcji zdefiniowanych poza bieżącym repozytorium:

$ (cd repoA && firebase deploy --only functions)
...
i  functions: preparing functions directory for uploading...
  functions: functions folder uploaded successfully
#  Gleefully ignores functions from repoB
i  functions: creating Node.js 16 function fnFromRepoA (us-central1)...
  Deploy Complete!

Zarządzanie wieloma pakietami źródłowymi (monorepo)

Właściwość codebase ułatwia zarządzanie wieloma pakietami źródłowymi w 1 repozytorium. Przeanalizujmy przypadek, w którym masz katalog projektu Firebase z definicjami funkcji rozłożonymi w kilku podrzędnych pakietach:

$  tree .
├── firebase.json
├── teamA
│   ├── index.js
│   └── package.json
└── teamB
    ├── index.js
    └── package.json

Ta konfiguracja pasuje do tych przypadków użycia:

  • Masz konfigurację monorepo i różne zespoły zarządzają własnymi definicjami funkcji w odizolowanym pakiecie.
  • Masz funkcję z silną zależnością zewnętrzną i długotrwałą inicjacją i chcesz odizolować ją od innych funkcji wrażliwych na opóźnienia.

Aby umożliwić konfigurację monrepo, taką jak ta, zdefiniuj wiele konfiguracji funkcji w firebase.json:

"functions": [
  {
    "source": "teamA",
    "codebase": "team-a"
  },
  {
    "source": "teamB",
    "codebase": "team-b"
  },
]

W tej konfiguracji interfejs wiersza poleceń Firebase wdraża funkcje ze wszystkich pakietów w ramach jednego polecenia wdrożenia:

$ firebase deploy --only functions
i  deploying functions
i  functions: preparing codebase team-a for deployment
i  functions: preparing codebase team-b for deployment
i  functions: creating Node.js 16 function team-a:helloATeam(us-central1)...
i  functions: creating Node.js 16 function team-b:helloBTeam(us-central1)...
...

Możesz też wdrożyć konkretną bazę kodu:

$ firebase deploy --only functions:team-b
i  deploying functions
i  functions: preparing codebase team-b for deployment
i  functions: updating Node.js 16 function team-b:helloBTeam(us-central1)...
...

Pisanie funkcji w wielu plikach

Gdy dopiero zaczynasz korzystać z Cloud Functions, możesz umieścić pierwsze kilka funkcji w jednym pliku:

index.js

const functions = require('firebase-functions/v1');
exports.foo = functions.https.onRequest((request, response) => {
  // ...
});
exports.bar = functions.https.onRequest((request, response) => {
  // ...
});

main.py

from firebase_functions import https_fn

@https_fn.on_request()
def foo(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello foo!")

@https_fn.on_request()
def bar(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello bar!")

W przypadku większej liczby funkcji może być trudno zarządzać tymi funkcjami. Zamiast tego możesz umieścić całą logikę każdej funkcji w osobnym pliku i użyć pliku źródłowego jako listy eksportów:

Node.js

foo.js

const functions = require('firebase-functions/v1');
exports.foo = functions.https.onRequest((request, response) => {
  // ...
});

bar.js

const functions = require('firebase-functions/v1');
exports.bar = functions.https.onRequest((request, response) => {
  // ...
});

index.js

const foo = require('./foo');
const bar = require('./bar');
exports.foo = foo.foo;
exports.bar = bar.bar;

Python

foo.py

from firebase_functions import https_fn

@https_fn.on_request()
def foo(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello foo!")

bar.py

from firebase_functions import https_fn

@https_fn.on_request()
def bar(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello foo!")

main.py

from fn_impl.foo import *
from fn_impl.bar import *

Ta konfiguracja zakłada strukturę katalogu projektu podobną do tej:

my-project
├── firebase.json
└── functions
    ├── fn_impl
    │   ├── __init__.py
    │   ├── foo.py
    │   └── bar.py
    ├── main.py
    └── requirements.txt

fn_impl: może mieć dowolną nazwę

__init__.py: wymagane, ale może być puste

Funkcje grupowe

W wielu projektach funkcje można rozdzielać na grupy logiczne, które należy wdrażać i utrzymywać razem. Możesz mieć np. grupę funkcji służących do raportowania danych:

metrics.js


const functions = require('firebase-functions/v1');
exports.usageStats = functions.https.onRequest((request, response) => {
  // ...
});
exports.nightlyReport = functions.https.onRequest((request, response) => {
  // ...
});

Podczas eksportowania w pliku index.js możesz umieścić te funkcje w grupie:

index.js


// Export both functions from metrics.js in the "metrics" group:
//  - metrics-usageStats
//  - metrics-nightlyReport
exports.metrics = require('./metrics');

Po wdrożeniu funkcje będą miały przedrostek odpowiadający nazwie grupy, więc w tym przykładzie funkcje będą miały nazwy metrics-usageStatsmetrics-nightlyReport.

Podczas wdrażania funkcji możesz ograniczyć działanie do jednej grupy:


firebase deploy --only functions:metrics

Dalsze kroki

Aby dowiedzieć się więcej o Cloud Functions, zapoznaj się z tymi artykułami: